@agentshouse/kit 0.1.0-alpha.21 → 0.1.0-alpha.23
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/README.md +48 -34
- package/bin/connect-linux.sh +8 -9
- package/bin/kit.js +80 -97
- package/bootstrap.json +3 -3
- package/package.json +1 -1
- package/src/bytes.js +16 -7
- package/src/contract.js +2 -0
- package/src/login.js +21 -10
- package/src/worker.js +57 -12
- package/src/working-copy.js +18 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ On Linux `amd64` or `arm64` with a working Docker Engine, run the published,
|
|
|
10
10
|
versioned bootstrap once:
|
|
11
11
|
|
|
12
12
|
```sh
|
|
13
|
-
bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
13
|
+
bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.23/connect-linux.sh)
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
The release asset pins one immutable multi-architecture image digest. It creates
|
|
@@ -142,11 +142,18 @@ which the User wrote once this adoption's proof had passed. The adapter
|
|
|
142
142
|
packages themselves are Environment prerequisites, not Kit dependencies, and
|
|
143
143
|
carry no record.
|
|
144
144
|
|
|
145
|
-
The bootstrap invokes bare `kit login
|
|
145
|
+
The bootstrap invokes bare `kit login`, which connects to `https://agents.house`;
|
|
146
|
+
`--house <origin>` names another House. Login records the House in Kit home
|
|
147
|
+
beside the credential. Every House-bound command talks to that recorded House,
|
|
148
|
+
and every command but login refuses `--house` as an unknown argument. The first login names its new
|
|
146
149
|
Environment after the host computer and stores its Environment id in Kit home;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
the panel renames it. An explicit later login reconnects that retained id, and
|
|
151
|
+
`--environment <uuid>` reconnects the Environment it names instead, whether or
|
|
152
|
+
not Kit home records one, and Kit home then records that id. Reconnecting issues the next credential for the same Environment
|
|
153
|
+
and ends the previous one, including one `envs/revoke` ended; House refuses an
|
|
154
|
+
Environment it no longer holds with its cause and never creates a replacement.
|
|
155
|
+
A malformed id or an answer naming a different Environment is refused before
|
|
156
|
+
installation.
|
|
150
157
|
|
|
151
158
|
Use `--manual` when the browser cannot reach the Kit's loopback listener. The
|
|
152
159
|
command stores the issued Kit User credential in `$HOUSE_KIT_HOME/credential`
|
|
@@ -253,7 +260,7 @@ and the batch it produces, and the lease has to outlive both.
|
|
|
253
260
|
Apply the declared source state to House:
|
|
254
261
|
|
|
255
262
|
```sh
|
|
256
|
-
kit apply
|
|
263
|
+
kit apply
|
|
257
264
|
```
|
|
258
265
|
|
|
259
266
|
Only the state House declares - instances, services, `source_ref`, and Room
|
|
@@ -442,7 +449,7 @@ its owner asks it to renew what the Environment holds. The Kit acts on the
|
|
|
442
449
|
request it acquires, never on its own:
|
|
443
450
|
|
|
444
451
|
```sh
|
|
445
|
-
kit setup
|
|
452
|
+
kit setup
|
|
446
453
|
```
|
|
447
454
|
|
|
448
455
|
`kit worker run` performs the same step at the start of a pass, before it
|
|
@@ -558,7 +565,7 @@ command returns exactly one JSON object such as `{ "authorized": true }`.
|
|
|
558
565
|
## The Worker runtime
|
|
559
566
|
|
|
560
567
|
```sh
|
|
561
|
-
kit worker run
|
|
568
|
+
kit worker run
|
|
562
569
|
```
|
|
563
570
|
|
|
564
571
|
Worker-runtime readiness validates the tmux this Kit declares support for,
|
|
@@ -1112,8 +1119,8 @@ into the evidence each spec writes under `tmp/delivery-evidence/`.
|
|
|
1112
1119
|
## Capturing
|
|
1113
1120
|
|
|
1114
1121
|
```sh
|
|
1115
|
-
kit source capture acme
|
|
1116
|
-
kit source capture acme notes
|
|
1122
|
+
kit source capture acme # every declared service
|
|
1123
|
+
kit source capture acme notes # one service
|
|
1117
1124
|
```
|
|
1118
1125
|
|
|
1119
1126
|
Each run reads the checkpoint, acquires one bounded page, submits one batch,
|
|
@@ -1146,8 +1153,8 @@ Room and Record and exits non-zero while any stays unsaved; acquisition health
|
|
|
1146
1153
|
stays about acquisition, so a failed save never changes it.
|
|
1147
1154
|
|
|
1148
1155
|
```sh
|
|
1149
|
-
kit source retry-files acme
|
|
1150
|
-
kit source retry-files acme notes
|
|
1156
|
+
kit source retry-files acme # every declared service
|
|
1157
|
+
kit source retry-files acme notes # one service
|
|
1151
1158
|
```
|
|
1152
1159
|
|
|
1153
1160
|
`retry-files` saves only the references still outstanding. It runs no connector,
|
|
@@ -1203,8 +1210,7 @@ Git credential:
|
|
|
1203
1210
|
```sh
|
|
1204
1211
|
kit repository capture /srv/acme \
|
|
1205
1212
|
--room work-room \
|
|
1206
|
-
--source-handle acme-repository
|
|
1207
|
-
--house https://agents.house
|
|
1213
|
+
--source-handle acme-repository
|
|
1208
1214
|
```
|
|
1209
1215
|
|
|
1210
1216
|
The command runs ordinary `git pull` in the checkout with the local User's Git
|
|
@@ -1306,6 +1312,19 @@ control lease when a resident runs, and which otherwise acquires and releases
|
|
|
1306
1312
|
its own. A Session refuses `--owner`, and a missing bridge is never replaced by
|
|
1307
1313
|
owner authority.
|
|
1308
1314
|
|
|
1315
|
+
A Session's edit too large for one MCP request of 4 MiB goes through House's
|
|
1316
|
+
`prepare_edit_upload` Command on that Session's bridge, naming every changed
|
|
1317
|
+
path, the byte count and the SHA-256 of the bytes to upload under the push's
|
|
1318
|
+
operation. Those bytes are the serialized changes, padded with trailing spaces
|
|
1319
|
+
to one byte over 4 MiB when only the request around them passed the bound,
|
|
1320
|
+
because House takes no smaller upload. Kit sends them to the upload address
|
|
1321
|
+
House returns and then calls `edit` with the paths and the returned upload
|
|
1322
|
+
instead of the changes, so House applies them as the same one edit. The upload
|
|
1323
|
+
is recorded in Kit home before the edit is sent, so a push whose answer was lost
|
|
1324
|
+
resends that same edit; when House no longer holds the uploaded bytes and has
|
|
1325
|
+
not applied the edit, the push uploads them again once under the same operation.
|
|
1326
|
+
A change set beyond House's batch bounds is refused whole and never split.
|
|
1327
|
+
|
|
1309
1328
|
Inside an Agent Session `git` is Kit's shim in front of the installed Git, and
|
|
1310
1329
|
every command runs exactly as that Git runs it. In a copy or one of its linked
|
|
1311
1330
|
worktrees the shim compares that worktree's index before and after each
|
|
@@ -1387,13 +1406,12 @@ beside `library/**`.
|
|
|
1387
1406
|
|
|
1388
1407
|
```sh
|
|
1389
1408
|
kit sync bootstrap ./replica \
|
|
1390
|
-
--house https://agents.house \
|
|
1391
1409
|
--prefix /rooms/work/library \
|
|
1392
1410
|
--prefix /rooms/work/capture \
|
|
1393
1411
|
--prefix /private/library
|
|
1394
|
-
kit sync pull ./replica
|
|
1395
|
-
kit sync submit ./replica --
|
|
1396
|
-
kit sync rebootstrap ./replica
|
|
1412
|
+
kit sync pull ./replica
|
|
1413
|
+
kit sync submit ./replica --changes ./changes.json
|
|
1414
|
+
kit sync rebootstrap ./replica
|
|
1397
1415
|
```
|
|
1398
1416
|
|
|
1399
1417
|
`bootstrap` writes `ROOM.md`, Library text, House-derived projections, and
|
|
@@ -1456,16 +1474,13 @@ absolute paths.
|
|
|
1456
1474
|
|
|
1457
1475
|
```sh
|
|
1458
1476
|
kit attachment upload ./notes.bin \
|
|
1459
|
-
--house https://agents.house \
|
|
1460
1477
|
--room room:work \
|
|
1461
1478
|
--media-type application/pdf
|
|
1462
1479
|
|
|
1463
1480
|
kit attachment upload ./notes.bin \
|
|
1464
|
-
--house https://agents.house \
|
|
1465
1481
|
--private
|
|
1466
1482
|
|
|
1467
1483
|
kit attachment get \
|
|
1468
|
-
--house https://agents.house \
|
|
1469
1484
|
--attachment at_1x.q \
|
|
1470
1485
|
--room room:work \
|
|
1471
1486
|
--output ./notes.bin
|
|
@@ -1511,7 +1526,7 @@ Any missing prerequisite refuses the create before House ever presents the
|
|
|
1511
1526
|
workspace as constrained, naming the exact missing piece.
|
|
1512
1527
|
|
|
1513
1528
|
```sh
|
|
1514
|
-
kit workspace host
|
|
1529
|
+
kit workspace host
|
|
1515
1530
|
```
|
|
1516
1531
|
|
|
1517
1532
|
The command acquires this host's workspace work under its Environment control
|
|
@@ -1566,7 +1581,7 @@ image from the original the Session stored.
|
|
|
1566
1581
|
Bind the bot to this Environment:
|
|
1567
1582
|
|
|
1568
1583
|
```sh
|
|
1569
|
-
kit telegram connect
|
|
1584
|
+
kit telegram connect
|
|
1570
1585
|
```
|
|
1571
1586
|
|
|
1572
1587
|
The command asks for the token on a hidden terminal prompt, or reads one line
|
|
@@ -1585,7 +1600,7 @@ proven owner's Telegram user id.
|
|
|
1585
1600
|
Run the gateway:
|
|
1586
1601
|
|
|
1587
1602
|
```sh
|
|
1588
|
-
kit telegram serve
|
|
1603
|
+
kit telegram serve
|
|
1589
1604
|
```
|
|
1590
1605
|
|
|
1591
1606
|
The command holds one Environment control lease for the whole run and performs
|
|
@@ -1631,7 +1646,7 @@ command is resident and stops on SIGINT or SIGTERM, printing what it settled.
|
|
|
1631
1646
|
Release the bot:
|
|
1632
1647
|
|
|
1633
1648
|
```sh
|
|
1634
|
-
kit telegram disconnect
|
|
1649
|
+
kit telegram disconnect
|
|
1635
1650
|
```
|
|
1636
1651
|
|
|
1637
1652
|
The command writes a local disconnect marker first, deletes the webhook, removes
|
|
@@ -1677,8 +1692,8 @@ Disabling a service or an instance leaves its state in place, so re-enabling
|
|
|
1677
1692
|
resumes from the stored cursor instead of importing again.
|
|
1678
1693
|
|
|
1679
1694
|
```sh
|
|
1680
|
-
kit source reset acme
|
|
1681
|
-
kit source reset acme notes
|
|
1695
|
+
kit source reset acme # every service
|
|
1696
|
+
kit source reset acme notes # one service
|
|
1682
1697
|
```
|
|
1683
1698
|
|
|
1684
1699
|
Reset is the only deletion of captured state. It removes the named checkpoints
|
|
@@ -1715,7 +1730,7 @@ refused rather than read as no limit. A ceiling is a shared ceiling, never a
|
|
|
1715
1730
|
reservation, and House infers no supported Agent count from any of these values.
|
|
1716
1731
|
|
|
1717
1732
|
```sh
|
|
1718
|
-
kit host report
|
|
1733
|
+
kit host report
|
|
1719
1734
|
```
|
|
1720
1735
|
|
|
1721
1736
|
The command reads `/proc/stat` twice a second apart, reads `/proc/meminfo`, and
|
|
@@ -1737,7 +1752,7 @@ Description=Report measured host capacity to House
|
|
|
1737
1752
|
[Service]
|
|
1738
1753
|
Type=oneshot
|
|
1739
1754
|
User=house-kit
|
|
1740
|
-
ExecStart=/usr/bin/kit host report
|
|
1755
|
+
ExecStart=/usr/bin/kit host report
|
|
1741
1756
|
```
|
|
1742
1757
|
|
|
1743
1758
|
```
|
|
@@ -1786,11 +1801,10 @@ manifest, URLs, or attachments.
|
|
|
1786
1801
|
|
|
1787
1802
|
```sh
|
|
1788
1803
|
kit migrate emit my-import --package /home/ada/exports/project
|
|
1789
|
-
kit migrate mount /home/ada/exports/project --
|
|
1790
|
-
--subtree /rooms/work/library/project
|
|
1804
|
+
kit migrate mount /home/ada/exports/project --subtree /rooms/work/library/project
|
|
1791
1805
|
# Or emit and mount in one maintained mount session:
|
|
1792
1806
|
kit migrate run my-import --package /home/ada/exports/another-project \
|
|
1793
|
-
--
|
|
1807
|
+
--subtree /private/library/another-project
|
|
1794
1808
|
```
|
|
1795
1809
|
|
|
1796
1810
|
Emission refuses an existing output directory. The module reads one JSON line
|
|
@@ -1877,8 +1891,8 @@ leaves the pending mounted report plus the local outcomes. Re-migration is
|
|
|
1877
1891
|
explicit unmount followed by mount.
|
|
1878
1892
|
|
|
1879
1893
|
```sh
|
|
1880
|
-
kit migrate unmount /rooms/work/library/project
|
|
1881
|
-
kit migrate unmount /rooms/work/library/project
|
|
1894
|
+
kit migrate unmount /rooms/work/library/project
|
|
1895
|
+
kit migrate unmount /rooms/work/library/project \
|
|
1882
1896
|
--confirm /rooms/work/library/project
|
|
1883
1897
|
```
|
|
1884
1898
|
|
package/bin/connect-linux.sh
CHANGED
|
@@ -39,7 +39,7 @@ login_kit() {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
start_resident() {
|
|
42
|
-
docker run -d --name "$NAME" --restart unless-stopped --user "$(id -u):$(id -g)" --mount "$KIT_HOME_MOUNT" --mount "$WORKSPACE_MOUNT" --label "agentshouse.house=$HOUSE" --env HOME=/kit-home --env HOUSE_KIT_HOME=/kit-home --env HOUSE_KIT_CONTAINER=1 "$IMAGE" resident
|
|
42
|
+
docker run -d --name "$NAME" --restart unless-stopped --user "$(id -u):$(id -g)" --mount "$KIT_HOME_MOUNT" --mount "$WORKSPACE_MOUNT" --label "agentshouse.house=$HOUSE" --env HOME=/kit-home --env HOUSE_KIT_HOME=/kit-home --env HOUSE_KIT_CONTAINER=1 "$IMAGE" resident >/dev/null
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
if [[ "${1:-}" == kit || "${1:-}" == house ]]; then
|
|
@@ -168,7 +168,7 @@ if docker container inspect "$NAME" >/dev/null 2>&1; then
|
|
|
168
168
|
printf 'House Kit updated for Environment %s.\n' "$(cat "$KIT_HOME/environment")"
|
|
169
169
|
exit 0
|
|
170
170
|
fi
|
|
171
|
-
"${DOCKER_RUN[@]}" credential check
|
|
171
|
+
"${DOCKER_RUN[@]}" credential check
|
|
172
172
|
if [[ "$(docker inspect --format '{{.State.Running}}' "$NAME")" == true ]]; then
|
|
173
173
|
printf 'House Kit is already running for Environment %s.\n' "$(cat "$KIT_HOME/environment")"
|
|
174
174
|
else
|
|
@@ -185,15 +185,14 @@ if [[ ! -f "$KIT_HOME/kit.json" ]]; then
|
|
|
185
185
|
fi
|
|
186
186
|
|
|
187
187
|
if [[ -f "$KIT_HOME/credential" ]]; then
|
|
188
|
-
"${DOCKER_RUN[@]}" credential check
|
|
188
|
+
"${DOCKER_RUN[@]}" credential check
|
|
189
189
|
else
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
fi
|
|
190
|
+
LOGIN=()
|
|
191
|
+
if ((HOUSE_SELECTED)); then LOGIN+=(--house "$HOUSE"); fi
|
|
192
|
+
if ((MANUAL)); then LOGIN+=(--manual); fi
|
|
193
|
+
login_kit "${LOGIN[@]}"
|
|
195
194
|
fi
|
|
196
195
|
|
|
197
|
-
"${DOCKER_RUN[@]}" apply
|
|
196
|
+
"${DOCKER_RUN[@]}" apply
|
|
198
197
|
start_resident
|
|
199
198
|
printf 'House Kit connected for Environment %s.\n' "$(cat "$KIT_HOME/environment")"
|
package/bin/kit.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
|
-
import { connectHouse, storedEnvironment, storedHouse } from "../src/login.js";
|
|
5
|
+
import { connectHouse, loginHouse, storedEnvironment, storedHouse } from "../src/login.js";
|
|
6
6
|
import { apply } from "../src/apply.js";
|
|
7
7
|
import { captureSource, resetSource, retrySourceOriginals, sourceStatus } from "../src/source.js";
|
|
8
8
|
import { setupEnvironment } from "../src/setup.js";
|
|
@@ -32,44 +32,44 @@ const USAGE =
|
|
|
32
32
|
"usage: kit <login|resident|credential|setup|apply|agents|source|repository|worker|sync|migrate|attachment|host|workspace|telegram> ...";
|
|
33
33
|
const LOGIN_USAGE =
|
|
34
34
|
"usage: kit login [--house <url>] [--environment <uuid>] [--manual|--host-browser]";
|
|
35
|
-
const HOST_USAGE = "usage: kit host report [--
|
|
35
|
+
const HOST_USAGE = "usage: kit host report [--config <path>]";
|
|
36
36
|
const AGENTS_USAGE =
|
|
37
|
-
"usage: kit agents update [--
|
|
38
|
-
const WORKSPACE_USAGE = "usage: kit workspace host [--
|
|
37
|
+
"usage: kit agents update [--config <path>] | kit agents sign-in <kind> [--config <path>]";
|
|
38
|
+
const WORKSPACE_USAGE = "usage: kit workspace host [--config <path>] [--passes <count>]";
|
|
39
39
|
const SOURCE_USAGE =
|
|
40
|
-
"usage: kit source <capture|reset|retry-files> <instance> [service] [--
|
|
40
|
+
"usage: kit source <capture|reset|retry-files> <instance> [service] [--config <path>]\n" +
|
|
41
41
|
" kit source status <instance> [service] [--config <path>]\n" +
|
|
42
42
|
" kit source sign-in <instance> [--config <path>]";
|
|
43
|
-
const SETUP_USAGE = "usage: kit setup [--
|
|
43
|
+
const SETUP_USAGE = "usage: kit setup [--config <path>]";
|
|
44
44
|
const REPOSITORY_USAGE =
|
|
45
|
-
"usage: kit repository capture <checkout> --room <room> --source-handle <handle>
|
|
45
|
+
"usage: kit repository capture <checkout> --room <room> --source-handle <handle>";
|
|
46
46
|
const WORKER_USAGE =
|
|
47
|
-
"usage: kit worker run [--
|
|
47
|
+
"usage: kit worker run [--config <path>] | kit worker attach [<session>]";
|
|
48
48
|
const SYNC_USAGE =
|
|
49
49
|
"usage: kit sync <bootstrap|pull|rebootstrap|submit> ...";
|
|
50
50
|
const SYNC_BOOTSTRAP_USAGE =
|
|
51
|
-
"usage: kit sync bootstrap <directory>
|
|
52
|
-
const SYNC_PULL_USAGE = "usage: kit sync pull <directory>
|
|
53
|
-
const SYNC_REBOOTSTRAP_USAGE = "usage: kit sync rebootstrap <directory>
|
|
51
|
+
"usage: kit sync bootstrap <directory> --prefix <path> [--prefix <path> ...]";
|
|
52
|
+
const SYNC_PULL_USAGE = "usage: kit sync pull <directory>";
|
|
53
|
+
const SYNC_REBOOTSTRAP_USAGE = "usage: kit sync rebootstrap <directory>";
|
|
54
54
|
const SYNC_SUBMIT_USAGE =
|
|
55
|
-
"usage: kit sync submit <directory> [--
|
|
55
|
+
"usage: kit sync submit <directory> [--changes <path>]";
|
|
56
56
|
const MIGRATE_USAGE = "usage: kit migrate <emit|mount|unmount|run> ...";
|
|
57
57
|
const MIGRATE_EMIT_USAGE =
|
|
58
58
|
"usage: kit migrate emit <migrator> --package <directory> [--config <path>]";
|
|
59
59
|
const MIGRATE_MOUNT_USAGE =
|
|
60
|
-
"usage: kit migrate mount <package>
|
|
60
|
+
"usage: kit migrate mount <package> --subtree <path>";
|
|
61
61
|
const MIGRATE_UNMOUNT_USAGE =
|
|
62
|
-
"usage: kit migrate unmount <subtree> [--
|
|
62
|
+
"usage: kit migrate unmount <subtree> [--confirm <subtree>]";
|
|
63
63
|
const MIGRATE_RUN_USAGE =
|
|
64
|
-
"usage: kit migrate run <migrator>
|
|
64
|
+
"usage: kit migrate run <migrator> --subtree <path> --package <directory> [--config <path>]";
|
|
65
65
|
const ATTACHMENT_USAGE = "usage: kit attachment <upload|get> ...";
|
|
66
66
|
const ATTACHMENT_UPLOAD_USAGE =
|
|
67
|
-
"usage: kit attachment upload <file>
|
|
67
|
+
"usage: kit attachment upload <file> (--room <ref> | --private) [--name <name>] [--media-type <type>]";
|
|
68
68
|
const ATTACHMENT_GET_USAGE =
|
|
69
|
-
"usage: kit attachment get
|
|
69
|
+
"usage: kit attachment get --attachment <at_ref> (--room <ref> | --private) [--output <path>]";
|
|
70
70
|
const TELEGRAM_USAGE =
|
|
71
|
-
"usage: kit telegram <connect|disconnect
|
|
72
|
-
" kit telegram serve [--
|
|
71
|
+
"usage: kit telegram <connect|disconnect>\n" +
|
|
72
|
+
" kit telegram serve [--passes <count>]";
|
|
73
73
|
|
|
74
74
|
function declared(argv, accepted, positionals, options = {}) {
|
|
75
75
|
const repeated = options.repeated ?? [];
|
|
@@ -115,14 +115,10 @@ function builtGoogleOAuthClient() {
|
|
|
115
115
|
return GOOGLE_OAUTH_CLIENT;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
async function selectedHouse(values, directory) {
|
|
119
|
-
return storedHouse(directory, false, values["--house"]);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
118
|
async function runLogin(args, directory) {
|
|
123
119
|
const { values } = declared(args, ["--house", "--manual", "--host-browser", "--environment"], 0, { flags: ["--host-browser"] });
|
|
124
120
|
if (values["--manual"] && values["--host-browser"]) throw new Error(LOGIN_USAGE);
|
|
125
|
-
const house = await
|
|
121
|
+
const house = await loginHouse(directory, values["--house"]);
|
|
126
122
|
const presentation = values["--manual"] === true ? "manual" : "loopback";
|
|
127
123
|
await connectHouse({
|
|
128
124
|
house,
|
|
@@ -135,9 +131,9 @@ async function runLogin(args, directory) {
|
|
|
135
131
|
}
|
|
136
132
|
|
|
137
133
|
async function runCredentialCheck(args, directory) {
|
|
138
|
-
|
|
134
|
+
declared(args, [], 0, { exact: true });
|
|
139
135
|
if (await storedEnvironment(directory) === null) throw new KitRefusal("kit_environment_invalid", "no enrolled Environment id");
|
|
140
|
-
const house = await
|
|
136
|
+
const house = await storedHouse(directory);
|
|
141
137
|
let session;
|
|
142
138
|
try {
|
|
143
139
|
session = await openSession({ house, directory, acquire: true });
|
|
@@ -153,8 +149,8 @@ async function runCredentialCheck(args, directory) {
|
|
|
153
149
|
}
|
|
154
150
|
|
|
155
151
|
async function runResidentCommand(args, directory) {
|
|
156
|
-
const { values } = declared(args, ["--
|
|
157
|
-
const house = await
|
|
152
|
+
const { values } = declared(args, ["--config"], 0, { exact: true });
|
|
153
|
+
const house = await storedHouse(directory);
|
|
158
154
|
const cancellation = new AbortController();
|
|
159
155
|
const stop = () => cancellation.abort();
|
|
160
156
|
process.once("SIGINT", stop);
|
|
@@ -185,9 +181,8 @@ async function runResidentDrain(args, directory) {
|
|
|
185
181
|
}
|
|
186
182
|
|
|
187
183
|
async function runSetup(args, directory) {
|
|
188
|
-
const { values } = declared(args, ["--
|
|
189
|
-
const house = await
|
|
190
|
-
if (!house) throw new Error(SETUP_USAGE);
|
|
184
|
+
const { values } = declared(args, ["--config"], 0, { exact: true });
|
|
185
|
+
const house = await storedHouse(directory);
|
|
191
186
|
const cancellation = new AbortController();
|
|
192
187
|
const cancel = () => cancellation.abort();
|
|
193
188
|
process.once("SIGINT", cancel);
|
|
@@ -240,9 +235,8 @@ async function runSourceSignIn(args, directory) {
|
|
|
240
235
|
}
|
|
241
236
|
|
|
242
237
|
async function runApply(args, directory) {
|
|
243
|
-
const { values } = declared(args, ["--
|
|
244
|
-
const house = await
|
|
245
|
-
if (!house) throw new Error("usage: kit apply --house <url> [--config <path>]");
|
|
238
|
+
const { values } = declared(args, ["--config"], 0);
|
|
239
|
+
const house = await storedHouse(directory);
|
|
246
240
|
const cancellation = new AbortController();
|
|
247
241
|
const cancel = () => cancellation.abort();
|
|
248
242
|
process.once("SIGINT", cancel);
|
|
@@ -269,9 +263,9 @@ async function runApply(args, directory) {
|
|
|
269
263
|
}
|
|
270
264
|
|
|
271
265
|
async function runSourceCapture(args, directory) {
|
|
272
|
-
const { values, rest } = declared(args, ["--
|
|
273
|
-
|
|
274
|
-
|
|
266
|
+
const { values, rest } = declared(args, ["--config"], 2);
|
|
267
|
+
if (rest.length === 0) throw new Error(SOURCE_USAGE);
|
|
268
|
+
const house = await storedHouse(directory);
|
|
275
269
|
const cancellation = new AbortController();
|
|
276
270
|
process.once("SIGINT", () => cancellation.abort());
|
|
277
271
|
const result = await captureSource(
|
|
@@ -314,9 +308,9 @@ function writeSourceOriginals(instance, service) {
|
|
|
314
308
|
}
|
|
315
309
|
|
|
316
310
|
async function runSourceRetryFiles(args, directory) {
|
|
317
|
-
const { values, rest } = declared(args, ["--
|
|
318
|
-
|
|
319
|
-
|
|
311
|
+
const { values, rest } = declared(args, ["--config"], 2);
|
|
312
|
+
if (rest.length === 0) throw new Error(SOURCE_USAGE);
|
|
313
|
+
const house = await storedHouse(directory);
|
|
320
314
|
const cancellation = new AbortController();
|
|
321
315
|
process.once("SIGINT", () => cancellation.abort());
|
|
322
316
|
const result = await retrySourceOriginals({
|
|
@@ -359,9 +353,9 @@ async function runSourceStatus(args, directory) {
|
|
|
359
353
|
}
|
|
360
354
|
|
|
361
355
|
async function runSourceReset(args, directory) {
|
|
362
|
-
const { values, rest } = declared(args, ["--
|
|
363
|
-
|
|
364
|
-
|
|
356
|
+
const { values, rest } = declared(args, ["--config"], 2);
|
|
357
|
+
if (rest.length === 0) throw new Error(SOURCE_USAGE);
|
|
358
|
+
const house = await storedHouse(directory);
|
|
365
359
|
const result = await resetSource({
|
|
366
360
|
house,
|
|
367
361
|
directory,
|
|
@@ -377,11 +371,11 @@ async function runSourceReset(args, directory) {
|
|
|
377
371
|
}
|
|
378
372
|
|
|
379
373
|
async function runRepositoryCapture(args, directory) {
|
|
380
|
-
const { values, rest } = declared(args, ["--
|
|
381
|
-
const house = await selectedHouse(values, directory);
|
|
374
|
+
const { values, rest } = declared(args, ["--room", "--source-handle"], 1);
|
|
382
375
|
const room = values["--room"];
|
|
383
376
|
const sourceHandle = values["--source-handle"];
|
|
384
|
-
if (!
|
|
377
|
+
if (!room || !sourceHandle || rest.length !== 1) throw new Error(REPOSITORY_USAGE);
|
|
378
|
+
const house = await storedHouse(directory);
|
|
385
379
|
const cancellation = new AbortController();
|
|
386
380
|
const cancel = () => cancellation.abort();
|
|
387
381
|
process.once("SIGINT", cancel);
|
|
@@ -429,9 +423,8 @@ function writeConversationFiles(supervised) {
|
|
|
429
423
|
}
|
|
430
424
|
|
|
431
425
|
async function runWorkerRun(args, directory) {
|
|
432
|
-
const { values } = declared(args, ["--
|
|
433
|
-
const house = await
|
|
434
|
-
if (!house) throw new Error(WORKER_USAGE);
|
|
426
|
+
const { values } = declared(args, ["--config"], 0);
|
|
427
|
+
const house = await storedHouse(directory);
|
|
435
428
|
const supervised = await runWorkers(
|
|
436
429
|
{
|
|
437
430
|
house,
|
|
@@ -500,12 +493,12 @@ function syncCancellation() {
|
|
|
500
493
|
}
|
|
501
494
|
|
|
502
495
|
async function runSyncBootstrap(args, directory) {
|
|
503
|
-
const {
|
|
496
|
+
const { lists, rest } = declared(args, ["--prefix"], 1, {
|
|
504
497
|
repeated: ["--prefix"],
|
|
505
498
|
exact: true,
|
|
506
499
|
});
|
|
507
|
-
|
|
508
|
-
|
|
500
|
+
if (lists["--prefix"].length === 0) throw new Error(SYNC_BOOTSTRAP_USAGE);
|
|
501
|
+
const house = await storedHouse(directory);
|
|
509
502
|
const run = syncCancellation();
|
|
510
503
|
try {
|
|
511
504
|
const result = await bootstrap({
|
|
@@ -524,9 +517,8 @@ async function runSyncBootstrap(args, directory) {
|
|
|
524
517
|
}
|
|
525
518
|
|
|
526
519
|
async function runSyncPull(args, directory) {
|
|
527
|
-
const {
|
|
528
|
-
const house = await
|
|
529
|
-
if (!house) throw new Error(SYNC_PULL_USAGE);
|
|
520
|
+
const { rest } = declared(args, [], 1, { exact: true });
|
|
521
|
+
const house = await storedHouse(directory);
|
|
530
522
|
const run = syncCancellation();
|
|
531
523
|
try {
|
|
532
524
|
const result = await pull({
|
|
@@ -544,9 +536,8 @@ async function runSyncPull(args, directory) {
|
|
|
544
536
|
}
|
|
545
537
|
|
|
546
538
|
async function runSyncRebootstrap(args, directory) {
|
|
547
|
-
const {
|
|
548
|
-
const house = await
|
|
549
|
-
if (!house) throw new Error(SYNC_REBOOTSTRAP_USAGE);
|
|
539
|
+
const { rest } = declared(args, [], 1, { exact: true });
|
|
540
|
+
const house = await storedHouse(directory);
|
|
550
541
|
const run = syncCancellation();
|
|
551
542
|
try {
|
|
552
543
|
const result = await rebootstrap({
|
|
@@ -571,9 +562,8 @@ async function readChanges(path) {
|
|
|
571
562
|
}
|
|
572
563
|
|
|
573
564
|
async function runSyncSubmit(args, directory) {
|
|
574
|
-
const { values, rest } = declared(args, ["--
|
|
575
|
-
const house = await
|
|
576
|
-
if (!house) throw new Error(SYNC_SUBMIT_USAGE);
|
|
565
|
+
const { values, rest } = declared(args, ["--changes"], 1, { exact: true });
|
|
566
|
+
const house = await storedHouse(directory);
|
|
577
567
|
const run = syncCancellation();
|
|
578
568
|
try {
|
|
579
569
|
const result = await submit({
|
|
@@ -646,11 +636,11 @@ async function mountEmitted(packagePath, options, run) {
|
|
|
646
636
|
}
|
|
647
637
|
|
|
648
638
|
async function runMigrateMount(args, directory) {
|
|
649
|
-
const { values, rest } = declared(args, ["--
|
|
650
|
-
|
|
651
|
-
if (!house || rest.length !== 1 || values["--subtree"] === undefined) {
|
|
639
|
+
const { values, rest } = declared(args, ["--subtree"], 1, { exact: true });
|
|
640
|
+
if (rest.length !== 1 || values["--subtree"] === undefined) {
|
|
652
641
|
throw new Error(MIGRATE_MOUNT_USAGE);
|
|
653
642
|
}
|
|
643
|
+
const house = await storedHouse(directory);
|
|
654
644
|
const run = migrateCancellation();
|
|
655
645
|
try {
|
|
656
646
|
await mountEmitted(rest[0], { house, directory, subtree: values["--subtree"] }, run);
|
|
@@ -660,18 +650,17 @@ async function runMigrateMount(args, directory) {
|
|
|
660
650
|
}
|
|
661
651
|
|
|
662
652
|
async function runMigrateRun(args, directory) {
|
|
663
|
-
const { values, rest } = declared(args, ["--
|
|
653
|
+
const { values, rest } = declared(args, ["--subtree", "--package", "--config"], 1, {
|
|
664
654
|
exact: true,
|
|
665
655
|
});
|
|
666
|
-
const house = await selectedHouse(values, directory);
|
|
667
656
|
if (
|
|
668
|
-
!house ||
|
|
669
657
|
rest.length !== 1 ||
|
|
670
658
|
values["--subtree"] === undefined ||
|
|
671
659
|
values["--package"] === undefined
|
|
672
660
|
) {
|
|
673
661
|
throw new Error(MIGRATE_RUN_USAGE);
|
|
674
662
|
}
|
|
663
|
+
const house = await storedHouse(directory);
|
|
675
664
|
const run = migrateCancellation();
|
|
676
665
|
try {
|
|
677
666
|
const emitted = await emitPackage(
|
|
@@ -688,9 +677,9 @@ async function runMigrateRun(args, directory) {
|
|
|
688
677
|
}
|
|
689
678
|
|
|
690
679
|
async function runMigrateUnmount(args, directory) {
|
|
691
|
-
const { values, rest } = declared(args, ["--
|
|
692
|
-
|
|
693
|
-
|
|
680
|
+
const { values, rest } = declared(args, ["--confirm"], 1, { exact: true });
|
|
681
|
+
if (rest.length !== 1) throw new Error(MIGRATE_UNMOUNT_USAGE);
|
|
682
|
+
const house = await storedHouse(directory);
|
|
694
683
|
const run = migrateCancellation();
|
|
695
684
|
try {
|
|
696
685
|
const result = await unmountSubtree({
|
|
@@ -746,12 +735,12 @@ function attachmentCancellation() {
|
|
|
746
735
|
async function runAttachmentUpload(args, directory) {
|
|
747
736
|
const { values, rest } = declared(
|
|
748
737
|
args,
|
|
749
|
-
["--
|
|
738
|
+
["--room", "--private", "--name", "--media-type"],
|
|
750
739
|
1,
|
|
751
740
|
{ flags: ["--private"] },
|
|
752
741
|
);
|
|
753
|
-
|
|
754
|
-
|
|
742
|
+
if (rest.length !== 1) throw new Error(ATTACHMENT_UPLOAD_USAGE);
|
|
743
|
+
const house = await storedHouse(directory);
|
|
755
744
|
const run = attachmentCancellation();
|
|
756
745
|
try {
|
|
757
746
|
const result = await uploadAttachment({
|
|
@@ -773,13 +762,13 @@ async function runAttachmentUpload(args, directory) {
|
|
|
773
762
|
async function runAttachmentGet(args, directory) {
|
|
774
763
|
const { values } = declared(
|
|
775
764
|
args,
|
|
776
|
-
["--
|
|
765
|
+
["--attachment", "--room", "--private", "--output"],
|
|
777
766
|
0,
|
|
778
767
|
{ flags: ["--private"] },
|
|
779
768
|
);
|
|
780
|
-
const house = await selectedHouse(values, directory);
|
|
781
769
|
const attachment = values["--attachment"];
|
|
782
|
-
if (!
|
|
770
|
+
if (!attachment) throw new Error(ATTACHMENT_GET_USAGE);
|
|
771
|
+
const house = await storedHouse(directory);
|
|
783
772
|
const run = attachmentCancellation();
|
|
784
773
|
try {
|
|
785
774
|
const result = await getAttachment({
|
|
@@ -805,9 +794,8 @@ async function runAttachment(args, directory) {
|
|
|
805
794
|
}
|
|
806
795
|
|
|
807
796
|
async function runHostReport(args, directory) {
|
|
808
|
-
const { values } = declared(args, ["--
|
|
809
|
-
const house = await
|
|
810
|
-
if (!house) throw new Error(HOST_USAGE);
|
|
797
|
+
const { values } = declared(args, ["--config"], 0, { exact: true });
|
|
798
|
+
const house = await storedHouse(directory);
|
|
811
799
|
const observed = await reportHost({
|
|
812
800
|
house,
|
|
813
801
|
directory,
|
|
@@ -819,9 +807,8 @@ async function runHostReport(args, directory) {
|
|
|
819
807
|
}
|
|
820
808
|
|
|
821
809
|
async function runAgentsUpdate(args, directory) {
|
|
822
|
-
const { values } = declared(args, ["--
|
|
823
|
-
const house = await
|
|
824
|
-
if (!house) throw new Error(AGENTS_USAGE);
|
|
810
|
+
const { values } = declared(args, ["--config"], 0, { exact: true });
|
|
811
|
+
const house = await storedHouse(directory);
|
|
825
812
|
const { updated } = await updateAgents({
|
|
826
813
|
house,
|
|
827
814
|
directory,
|
|
@@ -847,10 +834,10 @@ async function runAgentsUpdate(args, directory) {
|
|
|
847
834
|
}
|
|
848
835
|
|
|
849
836
|
async function runAgentsSignIn(args, directory) {
|
|
850
|
-
const { values, rest } = declared(args, ["--
|
|
851
|
-
const house = await selectedHouse(values, directory);
|
|
837
|
+
const { values, rest } = declared(args, ["--config"], 1, { exact: true });
|
|
852
838
|
const [kind] = rest;
|
|
853
|
-
if (!
|
|
839
|
+
if (!kind) throw new Error(AGENTS_USAGE);
|
|
840
|
+
const house = await storedHouse(directory);
|
|
854
841
|
const { status, reported } = await signInAgent({
|
|
855
842
|
house,
|
|
856
843
|
directory,
|
|
@@ -867,13 +854,12 @@ async function runAgentsSignIn(args, directory) {
|
|
|
867
854
|
}
|
|
868
855
|
|
|
869
856
|
async function runWorkspaceHost(args, directory) {
|
|
870
|
-
const { values } = declared(args, ["--
|
|
871
|
-
const house = await selectedHouse(values, directory);
|
|
872
|
-
if (!house) throw new Error(WORKSPACE_USAGE);
|
|
857
|
+
const { values } = declared(args, ["--config", "--passes"], 0, { exact: true });
|
|
873
858
|
const passes = values["--passes"] === undefined ? undefined : Number(values["--passes"]);
|
|
874
859
|
if (passes !== undefined && (!Number.isSafeInteger(passes) || passes < 1)) {
|
|
875
860
|
throw new Error(WORKSPACE_USAGE);
|
|
876
861
|
}
|
|
862
|
+
const house = await storedHouse(directory);
|
|
877
863
|
const served = await serveWorkspaces({
|
|
878
864
|
house,
|
|
879
865
|
directory,
|
|
@@ -894,9 +880,8 @@ async function runWorkspaceHost(args, directory) {
|
|
|
894
880
|
}
|
|
895
881
|
|
|
896
882
|
async function runTelegramConnect(args, directory) {
|
|
897
|
-
|
|
898
|
-
const house = await
|
|
899
|
-
if (!house) throw new Error(TELEGRAM_USAGE);
|
|
883
|
+
declared(args, [], 0, { exact: true });
|
|
884
|
+
const house = await storedHouse(directory);
|
|
900
885
|
const cancellation = new AbortController();
|
|
901
886
|
const cancel = () => cancellation.abort();
|
|
902
887
|
process.once("SIGINT", cancel);
|
|
@@ -914,9 +899,8 @@ async function runTelegramConnect(args, directory) {
|
|
|
914
899
|
}
|
|
915
900
|
|
|
916
901
|
async function runTelegramDisconnect(args, directory) {
|
|
917
|
-
|
|
918
|
-
const house = await
|
|
919
|
-
if (!house) throw new Error(TELEGRAM_USAGE);
|
|
902
|
+
declared(args, [], 0, { exact: true });
|
|
903
|
+
const house = await storedHouse(directory);
|
|
920
904
|
const cancellation = new AbortController();
|
|
921
905
|
const cancel = () => cancellation.abort();
|
|
922
906
|
process.once("SIGINT", cancel);
|
|
@@ -936,13 +920,12 @@ async function runTelegramDisconnect(args, directory) {
|
|
|
936
920
|
}
|
|
937
921
|
|
|
938
922
|
async function runTelegramServe(args, directory) {
|
|
939
|
-
const { values } = declared(args, ["--
|
|
940
|
-
const house = await selectedHouse(values, directory);
|
|
941
|
-
if (!house) throw new Error(TELEGRAM_USAGE);
|
|
923
|
+
const { values } = declared(args, ["--passes"], 0, { exact: true });
|
|
942
924
|
const passes = values["--passes"] === undefined ? undefined : Number(values["--passes"]);
|
|
943
925
|
if (passes !== undefined && (!Number.isSafeInteger(passes) || passes < 1)) {
|
|
944
926
|
throw new Error(TELEGRAM_USAGE);
|
|
945
927
|
}
|
|
928
|
+
const house = await storedHouse(directory);
|
|
946
929
|
const cancellation = new AbortController();
|
|
947
930
|
const cancel = () => cancellation.abort();
|
|
948
931
|
process.once("SIGINT", cancel);
|
package/bootstrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.23",
|
|
4
4
|
"hosts": {
|
|
5
5
|
"linux": {
|
|
6
|
-
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
6
|
+
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.23/connect-linux.sh)",
|
|
7
7
|
"asset": {
|
|
8
8
|
"name": "connect-linux.sh",
|
|
9
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
9
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.23/connect-linux.sh"
|
|
10
10
|
},
|
|
11
11
|
"workspace": {
|
|
12
12
|
"argument": "--workspace",
|
package/package.json
CHANGED
package/src/bytes.js
CHANGED
|
@@ -31,6 +31,11 @@ async function downloadBody(response, signal) {
|
|
|
31
31
|
return Buffer.concat(chunks, size);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function framedBody(request) {
|
|
35
|
+
if (request.method !== "POST" || !Buffer.isBuffer(request.body)) throw new KitRefusal("kit_byte_body_invalid");
|
|
36
|
+
return Buffer.concat([Buffer.from(`${request.operation}\n`, "utf8"), request.body]);
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
export async function transferBytes(request, dependencies = {}) {
|
|
35
40
|
if (request.method !== "GET" && request.method !== "POST") {
|
|
36
41
|
throw new KitRefusal("kit_byte_method_invalid", request.method);
|
|
@@ -41,23 +46,27 @@ export async function transferBytes(request, dependencies = {}) {
|
|
|
41
46
|
if (typeof request.operation !== "string" || request.operation.length === 0) {
|
|
42
47
|
throw new KitRefusal("kit_byte_operation_invalid");
|
|
43
48
|
}
|
|
49
|
+
const framed = request.framed === true;
|
|
50
|
+
const body = framed ? framedBody(request) : request.body;
|
|
44
51
|
if (request.method === "POST") {
|
|
45
|
-
if (!Buffer.isBuffer(
|
|
46
|
-
if (
|
|
52
|
+
if (!Buffer.isBuffer(body)) throw new KitRefusal("kit_byte_body_invalid");
|
|
53
|
+
if (body.length > BYTE_GRANT_POST_BYTES_MAX) {
|
|
47
54
|
throw new KitRefusal("original_bytes_exceeded");
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
const fetcher = dependencies.fetch ?? fetch;
|
|
51
|
-
const headers =
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
const headers = framed
|
|
59
|
+
? { "content-type": "text/plain; charset=utf-8" }
|
|
60
|
+
: {
|
|
61
|
+
[BYTE_GRANT_OPERATION_HEADER]: request.operation,
|
|
62
|
+
...(request.method === "POST" ? { "content-type": "application/octet-stream" } : {}),
|
|
63
|
+
};
|
|
55
64
|
let response;
|
|
56
65
|
try {
|
|
57
66
|
response = await fetcher(request.url, {
|
|
58
67
|
method: request.method,
|
|
59
68
|
headers,
|
|
60
|
-
body: request.method === "POST" ?
|
|
69
|
+
body: request.method === "POST" ? body : undefined,
|
|
61
70
|
redirect: "manual",
|
|
62
71
|
signal: request.signal,
|
|
63
72
|
});
|
package/src/contract.js
CHANGED
|
@@ -308,6 +308,8 @@ export const WORKING_COPY_PUSH_METHOD = "house/git/push";
|
|
|
308
308
|
export const WORKING_COPY_INDEX_METHOD = "house/git/index";
|
|
309
309
|
export const WORKING_COPY_PUSH_HELP =
|
|
310
310
|
"Submit a Room commit to House; default HEAD. Use --owner only outside an Agent Session. Native git push is not a House remote.";
|
|
311
|
+
export const WORKING_COPY_UPLOAD_COMMAND = "prepare_edit_upload";
|
|
312
|
+
export const MCP_REQUEST_BODY_MAX_BYTES = 4 * 1024 * 1024;
|
|
311
313
|
|
|
312
314
|
export const MIGRATOR_CONTRACT = "kit-migrator/1";
|
|
313
315
|
export const MIGRATOR_CATEGORY = "migrator";
|
package/src/login.js
CHANGED
|
@@ -14,6 +14,7 @@ import { writeText } from "./state.js";
|
|
|
14
14
|
|
|
15
15
|
const ENVIRONMENT =
|
|
16
16
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
17
|
+
const DEFAULT_HOUSE = "https://agents.house";
|
|
17
18
|
|
|
18
19
|
function verifier() {
|
|
19
20
|
return randomBytes(32).toString("base64url");
|
|
@@ -112,19 +113,29 @@ function houseOrigin(value) {
|
|
|
112
113
|
return value;
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
let value;
|
|
116
|
+
async function recordedHouse(directory) {
|
|
117
117
|
try {
|
|
118
|
-
|
|
118
|
+
return (await readFile(join(directory, "house"), "utf8")).trim();
|
|
119
119
|
} catch (error) {
|
|
120
|
-
if (error.code === "ENOENT")
|
|
121
|
-
if ((firstLogin || requested !== undefined) &&
|
|
122
|
-
await storedEnvironment(directory) === null && !await storedCredentialPresent(directory)) {
|
|
123
|
-
return houseOrigin(requested ?? "https://agents.house");
|
|
124
|
-
}
|
|
125
|
-
}
|
|
120
|
+
if (error.code === "ENOENT") return null;
|
|
126
121
|
throw new KitRefusal("kit_house_invalid", error.code ?? "unreadable");
|
|
127
122
|
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function storedHouse(directory) {
|
|
126
|
+
const value = await recordedHouse(directory);
|
|
127
|
+
if (value === null) throw new KitRefusal("kit_house_invalid", "ENOENT");
|
|
128
|
+
return houseOrigin(value);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function loginHouse(directory, requested) {
|
|
132
|
+
const value = await recordedHouse(directory);
|
|
133
|
+
if (value === null) {
|
|
134
|
+
if (await storedEnvironment(directory) !== null || await storedCredentialPresent(directory)) {
|
|
135
|
+
throw new KitRefusal("kit_house_invalid", "ENOENT");
|
|
136
|
+
}
|
|
137
|
+
return houseOrigin(requested ?? DEFAULT_HOUSE);
|
|
138
|
+
}
|
|
128
139
|
houseOrigin(value);
|
|
129
140
|
if (requested !== undefined && requested !== value) {
|
|
130
141
|
throw new KitRefusal("kit_house_mismatch", "this Environment is bound to another House origin");
|
|
@@ -133,7 +144,7 @@ export async function storedHouse(directory, firstLogin = false, requested = und
|
|
|
133
144
|
}
|
|
134
145
|
|
|
135
146
|
export function enrollmentAct(options, retained, host) {
|
|
136
|
-
if (options.
|
|
147
|
+
if (options.environment !== undefined && !ENVIRONMENT.test(options.environment)) {
|
|
137
148
|
throw new KitRefusal("kit_login_rejected", ENROLLMENT_USAGE);
|
|
138
149
|
}
|
|
139
150
|
const environment = options.environment ?? retained;
|
package/src/worker.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { randomBytes } from "node:crypto";
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { setTimeout as delay } from "node:timers/promises";
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { readBindings, removeBinding, writeBinding } from "./binding.js";
|
|
12
12
|
import { readContinuation, readContinuationRuns, writeContinuation, removeContinuation } from "./continuation.js";
|
|
13
13
|
import { bridgedFrame, openBridge } from "./bridge.js";
|
|
14
|
+
import { transferBytes } from "./bytes.js";
|
|
14
15
|
import { assistantBlocks } from "./blocks.js";
|
|
15
16
|
import { retryConversationFiles } from "./conversation-originals.js";
|
|
16
17
|
import { openAssistantDrafts } from "./draft.js";
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
ownerCaller,
|
|
26
27
|
pushWorkingCopy,
|
|
27
28
|
recordIndexChange,
|
|
29
|
+
uploadedPaths,
|
|
28
30
|
workingCopyPaths,
|
|
29
31
|
} from "./working-copy.js";
|
|
30
32
|
import { readConfiguration } from "./configuration.js";
|
|
@@ -32,6 +34,7 @@ import { launchBase, readyLaunchDirectory } from "./directory.js";
|
|
|
32
34
|
import {
|
|
33
35
|
BRIDGE_NONCE_ENVIRONMENT,
|
|
34
36
|
BRIDGE_SOCKET_ENVIRONMENT,
|
|
37
|
+
MCP_REQUEST_BODY_MAX_BYTES,
|
|
35
38
|
PROVIDER_CONTINUATION_UNAVAILABLE,
|
|
36
39
|
WORKER_COMMAND_BATCH_MAX,
|
|
37
40
|
WORKER_COMMAND_POLL_MS,
|
|
@@ -42,6 +45,7 @@ import {
|
|
|
42
45
|
WORKER_SUPERVISION_POLL_MS,
|
|
43
46
|
WORKING_COPY_INDEX_METHOD,
|
|
44
47
|
WORKING_COPY_PUSH_METHOD,
|
|
48
|
+
WORKING_COPY_UPLOAD_COMMAND,
|
|
45
49
|
} from "./contract.js";
|
|
46
50
|
import { EpochReportRequired, formatCommandError, houseFailed, KitRefusal } from "./refusal.js";
|
|
47
51
|
import {
|
|
@@ -299,20 +303,61 @@ function acceptedWith(text) {
|
|
|
299
303
|
return { refused: null, written: [...written.values()] };
|
|
300
304
|
}
|
|
301
305
|
|
|
306
|
+
function toolFrame(name, args, operation) {
|
|
307
|
+
return bridgedFrame({ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name, arguments: args } }, operation);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function uploadBody(changes) {
|
|
311
|
+
const body = Buffer.from(JSON.stringify({ changes }), "utf8");
|
|
312
|
+
if (body.length > MCP_REQUEST_BODY_MAX_BYTES) return body;
|
|
313
|
+
return Buffer.concat([body, Buffer.alloc(MCP_REQUEST_BODY_MAX_BYTES + 1 - body.length, " ")]);
|
|
314
|
+
}
|
|
315
|
+
|
|
302
316
|
async function sessionPush(context, bound, authority, frame, signal) {
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
317
|
+
const call = async (sent, called) =>
|
|
318
|
+
callHouseMcp(context.session, await sessionAuthority(context.session, bound.session, authority, sent), called, sent);
|
|
319
|
+
const refused = (answer) =>
|
|
320
|
+
answer.error !== undefined
|
|
321
|
+
? refusedWith(answer.error.message ?? "kit_house_refused")
|
|
322
|
+
: answer.result?.isError === true
|
|
323
|
+
? refusedWith(answerText(answer.result))
|
|
324
|
+
: null;
|
|
325
|
+
const stage = async (body, sent) => {
|
|
326
|
+
const bytes = uploadBody(body.changes);
|
|
327
|
+
const sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
328
|
+
const prepare = { paths: uploadedPaths(body.changes), bytes: bytes.length, sha256 };
|
|
329
|
+
const prepared = await call(
|
|
330
|
+
sent,
|
|
331
|
+
toolFrame("run_command", { command: WORKING_COPY_UPLOAD_COMMAND, arguments: prepare }, body.operation_id),
|
|
332
|
+
);
|
|
333
|
+
const refusal = refused(prepared);
|
|
334
|
+
if (refusal !== null) return refusal;
|
|
335
|
+
const transfer = answerYaml(answerText(prepared.result))?.transfer;
|
|
336
|
+
const staged = (
|
|
337
|
+
await transferBytes(
|
|
338
|
+
{ url: transfer?.url, method: "POST", operation: transfer?.operation, body: bytes, framed: true, signal: sent },
|
|
339
|
+
context.session.dependencies,
|
|
340
|
+
)
|
|
341
|
+
).transfer;
|
|
342
|
+
if (typeof staged?.url !== "string" || typeof staged.operation !== "string") {
|
|
343
|
+
throw new KitRefusal("kit_house_answer_unreadable", WORKING_COPY_UPLOAD_COMMAND);
|
|
344
|
+
}
|
|
345
|
+
return { upload: { url: staged.url, operation: staged.operation, bytes: bytes.length, sha256 } };
|
|
346
|
+
};
|
|
347
|
+
const edit = async (body, sent, retain) => {
|
|
348
|
+
let { upload } = body;
|
|
349
|
+
const inline = toolFrame("edit", { changes: body.changes }, body.operation_id);
|
|
350
|
+
if (upload === undefined && Buffer.byteLength(JSON.stringify(inline)) > MCP_REQUEST_BODY_MAX_BYTES) {
|
|
351
|
+
const staged = await stage(body, sent);
|
|
352
|
+
if (staged.upload === undefined) return staged;
|
|
353
|
+
({ upload } = staged);
|
|
354
|
+
await retain(upload);
|
|
355
|
+
}
|
|
356
|
+
const answer = await call(
|
|
311
357
|
sent,
|
|
358
|
+
upload === undefined ? inline : toolFrame("edit", { paths: uploadedPaths(body.changes), upload }, body.operation_id),
|
|
312
359
|
);
|
|
313
|
-
|
|
314
|
-
const text = answerText(answer.result);
|
|
315
|
-
return answer.result?.isError === true ? refusedWith(text) : acceptedWith(text);
|
|
360
|
+
return refused(answer) ?? acceptedWith(answerText(answer.result));
|
|
316
361
|
};
|
|
317
362
|
let outcome;
|
|
318
363
|
try {
|
package/src/working-copy.js
CHANGED
|
@@ -914,6 +914,10 @@ function pushedPaths(changes) {
|
|
|
914
914
|
return new Set(changes.flatMap((change) => (change.op === "rename" ? [change.path, change.to] : [change.path])));
|
|
915
915
|
}
|
|
916
916
|
|
|
917
|
+
export function uploadedPaths(changes) {
|
|
918
|
+
return [...pushedPaths(changes)].sort();
|
|
919
|
+
}
|
|
920
|
+
|
|
917
921
|
async function enumerated(context, copy, since, signal) {
|
|
918
922
|
const answer = await door(
|
|
919
923
|
context.session,
|
|
@@ -1104,12 +1108,18 @@ async function expired(context, copy, text, signal) {
|
|
|
1104
1108
|
return finish(context, accepted, signal);
|
|
1105
1109
|
}
|
|
1106
1110
|
|
|
1107
|
-
async function settle(context, copy, caller, signal) {
|
|
1111
|
+
async function settle(context, copy, caller, signal, restaged = false) {
|
|
1108
1112
|
const { push } = copy;
|
|
1109
1113
|
if (!sameCaller(push.caller, caller.binding)) return orphaned(context, copy, signal);
|
|
1114
|
+
let staged = push.upload !== undefined;
|
|
1115
|
+
const retain = async (upload) => {
|
|
1116
|
+
staged = true;
|
|
1117
|
+
await writeCopy(context.directory, { ...copy, push: { ...push, upload } }, signal);
|
|
1118
|
+
};
|
|
1110
1119
|
let answer;
|
|
1111
1120
|
try {
|
|
1112
|
-
|
|
1121
|
+
const { operation_id, changes, upload } = push;
|
|
1122
|
+
answer = await caller.submit({ operation_id, changes, ...(upload === undefined ? {} : { upload }) }, signal, retain);
|
|
1113
1123
|
} catch (error) {
|
|
1114
1124
|
if (!definite(error)) {
|
|
1115
1125
|
throw new KitRefusal("kit_working_copy_push_uncertain", "run house git push again to recover its outcome");
|
|
@@ -1118,6 +1128,12 @@ async function settle(context, copy, caller, signal) {
|
|
|
1118
1128
|
answer = { refused: { code: error.code, text: formatCommandError(error), current: Array.isArray(current) ? current : null } };
|
|
1119
1129
|
}
|
|
1120
1130
|
const { refused } = answer;
|
|
1131
|
+
if (refused?.code === "upload_unavailable" && staged && !restaged) {
|
|
1132
|
+
const { upload, ...unstaged } = push;
|
|
1133
|
+
const cleared = { ...copy, push: unstaged };
|
|
1134
|
+
await writeCopy(context.directory, cleared, signal);
|
|
1135
|
+
return settle(context, cleared, caller, signal, true);
|
|
1136
|
+
}
|
|
1121
1137
|
if (refused?.code === "operation_expired") return expired(context, copy, refused.text, signal);
|
|
1122
1138
|
if (refused === null) {
|
|
1123
1139
|
const pushed = pushedPaths(push.changes);
|