@agentshouse/kit 0.1.0-alpha.20 → 0.1.0-alpha.22
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 +70 -40
- 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/contract.js +1 -1
- package/src/login.js +21 -10
- package/src/worker.js +20 -10
- package/src/working-copy.js +177 -33
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.22/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
|
|
@@ -1261,9 +1267,35 @@ committed.
|
|
|
1261
1267
|
Local edits, staging and commits change nothing in House until a commit is
|
|
1262
1268
|
pushed. `refs/house/received` records the House content last received; each
|
|
1263
1269
|
commit Kit wrote there, and each commit it acknowledged for a push, is a House
|
|
1264
|
-
state. The House revision of every file content Kit received, keyed by path
|
|
1265
|
-
Git blob, stays in Kit home under
|
|
1266
|
-
repository. A local commit is a local tree, never a House revision.
|
|
1270
|
+
state. The House revision of every file content Kit received, keyed by path,
|
|
1271
|
+
Git blob and the House state that received it, stays in Kit home under
|
|
1272
|
+
`state/working-copies/`, outside the repository. A local commit is a local tree, never a House revision.
|
|
1273
|
+
|
|
1274
|
+
The resident Kit keeps every active copy current. On each pass, and whenever
|
|
1275
|
+
it reads the saved choice, one `POST /kit/door/discover` tells it where each
|
|
1276
|
+
copied Room stands, and for a Room that moved on it reads the changes after the
|
|
1277
|
+
copy's recorded position through `POST /kit/door/enumerate`. A changed, new or
|
|
1278
|
+
removed authored file becomes a commit on `refs/house/received`, its House
|
|
1279
|
+
revision kept in Kit home; changed, new or removed read-only content, such as
|
|
1280
|
+
Capture with its provenance sidecar or a House projection, is written into or
|
|
1281
|
+
removed from the ignored local tree, and `.gitignore` and `.ignore` follow it.
|
|
1282
|
+
Delivery never touches the branch, the index or a tracked file, and a push
|
|
1283
|
+
waiting for its outcome or its local integration keeps its record. A push takes
|
|
1284
|
+
each file's revision from the newest House state in its own base's history that
|
|
1285
|
+
received that content, so a local commit keeps the base it was made on even when
|
|
1286
|
+
House returns to the same text under a newer revision. A file new to the copy is tracked when a write scope
|
|
1287
|
+
House declares for the Room covers it more closely than a declared protected
|
|
1288
|
+
path, and a local file already sitting where new read-only content arrives
|
|
1289
|
+
stays yours: Kit neither overwrites nor later removes it. When the write scopes
|
|
1290
|
+
or protected paths House declares for the Room change, Kit reads the Room again
|
|
1291
|
+
as a reselection does. A position older than House
|
|
1292
|
+
retains, or from before House's database was restored, makes Kit read the Room
|
|
1293
|
+
again as a reselection does; while House's replica has not reached the position
|
|
1294
|
+
the copy waits for the next pass. A Room House no longer answers under its
|
|
1295
|
+
recorded handle makes Kit read the saved choice again, which follows a handle
|
|
1296
|
+
change or marks the copy inactive, and a Room that lost sync is named on
|
|
1297
|
+
standard error as `operation_denied` and keeps what it holds. `kit worker run`
|
|
1298
|
+
delivers once, after reading the saved choice.
|
|
1267
1299
|
|
|
1268
1300
|
`house git push [commit]`, run anywhere inside a copy or one of its linked
|
|
1269
1301
|
worktrees, submits one commit, `HEAD` by default, resolved once. Kit compares
|
|
@@ -1319,7 +1351,10 @@ commits, and pushes again. After acceptance Kit records House's result for the
|
|
|
1319
1351
|
pushed paths, the contents an owner push receives or each accepted revision
|
|
1320
1352
|
read by name, in a commit that covers the pushed one and in
|
|
1321
1353
|
`refs/house/received`; a read-only path House regenerates, such as the Wiki
|
|
1322
|
-
index, stays out of Git.
|
|
1354
|
+
index, stays out of Git. The copy's recorded position goes back to where it
|
|
1355
|
+
stood when the push was sent, so the next delivery brings again anything House
|
|
1356
|
+
changed after the push's result, including what delivery received while the
|
|
1357
|
+
push awaited its answer. Kit then
|
|
1323
1358
|
fast-forwards a clean branch still at the pushed commit, or rebases only its
|
|
1324
1359
|
later commits. A result not yet readable, uncommitted tracked changes,
|
|
1325
1360
|
a moved branch or a rebase conflict leave the branch as it was, with no stash or
|
|
@@ -1335,8 +1370,8 @@ Room again: if House holds the pushed content the push finishes as accepted,
|
|
|
1335
1370
|
otherwise it is dropped and the next push submits it as a new operation.
|
|
1336
1371
|
|
|
1337
1372
|
Deselecting a Room, or losing read access to it, marks its copy inactive:
|
|
1338
|
-
launches omit it, a new push is refused as
|
|
1339
|
-
nothing local is deleted; a push already sent still settles and integrates. Selecting it again reads the Room
|
|
1373
|
+
launches omit it, delivery stops, a new push is refused as
|
|
1374
|
+
`kit_working_copy_inactive`, and nothing local is deleted; a push already sent still settles and integrates. Selecting it again reads the Room
|
|
1340
1375
|
again, advances `refs/house/received`, and replaces only the read-only content
|
|
1341
1376
|
Kit wrote, never the branch, the index or a tracked file. A Room the owner may
|
|
1342
1377
|
read without sync is refused as `operation_denied` and stays uncopied, while its
|
|
@@ -1358,13 +1393,12 @@ beside `library/**`.
|
|
|
1358
1393
|
|
|
1359
1394
|
```sh
|
|
1360
1395
|
kit sync bootstrap ./replica \
|
|
1361
|
-
--house https://agents.house \
|
|
1362
1396
|
--prefix /rooms/work/library \
|
|
1363
1397
|
--prefix /rooms/work/capture \
|
|
1364
1398
|
--prefix /private/library
|
|
1365
|
-
kit sync pull ./replica
|
|
1366
|
-
kit sync submit ./replica --
|
|
1367
|
-
kit sync rebootstrap ./replica
|
|
1399
|
+
kit sync pull ./replica
|
|
1400
|
+
kit sync submit ./replica --changes ./changes.json
|
|
1401
|
+
kit sync rebootstrap ./replica
|
|
1368
1402
|
```
|
|
1369
1403
|
|
|
1370
1404
|
`bootstrap` writes `ROOM.md`, Library text, House-derived projections, and
|
|
@@ -1427,16 +1461,13 @@ absolute paths.
|
|
|
1427
1461
|
|
|
1428
1462
|
```sh
|
|
1429
1463
|
kit attachment upload ./notes.bin \
|
|
1430
|
-
--house https://agents.house \
|
|
1431
1464
|
--room room:work \
|
|
1432
1465
|
--media-type application/pdf
|
|
1433
1466
|
|
|
1434
1467
|
kit attachment upload ./notes.bin \
|
|
1435
|
-
--house https://agents.house \
|
|
1436
1468
|
--private
|
|
1437
1469
|
|
|
1438
1470
|
kit attachment get \
|
|
1439
|
-
--house https://agents.house \
|
|
1440
1471
|
--attachment at_1x.q \
|
|
1441
1472
|
--room room:work \
|
|
1442
1473
|
--output ./notes.bin
|
|
@@ -1482,7 +1513,7 @@ Any missing prerequisite refuses the create before House ever presents the
|
|
|
1482
1513
|
workspace as constrained, naming the exact missing piece.
|
|
1483
1514
|
|
|
1484
1515
|
```sh
|
|
1485
|
-
kit workspace host
|
|
1516
|
+
kit workspace host
|
|
1486
1517
|
```
|
|
1487
1518
|
|
|
1488
1519
|
The command acquires this host's workspace work under its Environment control
|
|
@@ -1537,7 +1568,7 @@ image from the original the Session stored.
|
|
|
1537
1568
|
Bind the bot to this Environment:
|
|
1538
1569
|
|
|
1539
1570
|
```sh
|
|
1540
|
-
kit telegram connect
|
|
1571
|
+
kit telegram connect
|
|
1541
1572
|
```
|
|
1542
1573
|
|
|
1543
1574
|
The command asks for the token on a hidden terminal prompt, or reads one line
|
|
@@ -1556,7 +1587,7 @@ proven owner's Telegram user id.
|
|
|
1556
1587
|
Run the gateway:
|
|
1557
1588
|
|
|
1558
1589
|
```sh
|
|
1559
|
-
kit telegram serve
|
|
1590
|
+
kit telegram serve
|
|
1560
1591
|
```
|
|
1561
1592
|
|
|
1562
1593
|
The command holds one Environment control lease for the whole run and performs
|
|
@@ -1602,7 +1633,7 @@ command is resident and stops on SIGINT or SIGTERM, printing what it settled.
|
|
|
1602
1633
|
Release the bot:
|
|
1603
1634
|
|
|
1604
1635
|
```sh
|
|
1605
|
-
kit telegram disconnect
|
|
1636
|
+
kit telegram disconnect
|
|
1606
1637
|
```
|
|
1607
1638
|
|
|
1608
1639
|
The command writes a local disconnect marker first, deletes the webhook, removes
|
|
@@ -1648,8 +1679,8 @@ Disabling a service or an instance leaves its state in place, so re-enabling
|
|
|
1648
1679
|
resumes from the stored cursor instead of importing again.
|
|
1649
1680
|
|
|
1650
1681
|
```sh
|
|
1651
|
-
kit source reset acme
|
|
1652
|
-
kit source reset acme notes
|
|
1682
|
+
kit source reset acme # every service
|
|
1683
|
+
kit source reset acme notes # one service
|
|
1653
1684
|
```
|
|
1654
1685
|
|
|
1655
1686
|
Reset is the only deletion of captured state. It removes the named checkpoints
|
|
@@ -1686,7 +1717,7 @@ refused rather than read as no limit. A ceiling is a shared ceiling, never a
|
|
|
1686
1717
|
reservation, and House infers no supported Agent count from any of these values.
|
|
1687
1718
|
|
|
1688
1719
|
```sh
|
|
1689
|
-
kit host report
|
|
1720
|
+
kit host report
|
|
1690
1721
|
```
|
|
1691
1722
|
|
|
1692
1723
|
The command reads `/proc/stat` twice a second apart, reads `/proc/meminfo`, and
|
|
@@ -1708,7 +1739,7 @@ Description=Report measured host capacity to House
|
|
|
1708
1739
|
[Service]
|
|
1709
1740
|
Type=oneshot
|
|
1710
1741
|
User=house-kit
|
|
1711
|
-
ExecStart=/usr/bin/kit host report
|
|
1742
|
+
ExecStart=/usr/bin/kit host report
|
|
1712
1743
|
```
|
|
1713
1744
|
|
|
1714
1745
|
```
|
|
@@ -1757,11 +1788,10 @@ manifest, URLs, or attachments.
|
|
|
1757
1788
|
|
|
1758
1789
|
```sh
|
|
1759
1790
|
kit migrate emit my-import --package /home/ada/exports/project
|
|
1760
|
-
kit migrate mount /home/ada/exports/project --
|
|
1761
|
-
--subtree /rooms/work/library/project
|
|
1791
|
+
kit migrate mount /home/ada/exports/project --subtree /rooms/work/library/project
|
|
1762
1792
|
# Or emit and mount in one maintained mount session:
|
|
1763
1793
|
kit migrate run my-import --package /home/ada/exports/another-project \
|
|
1764
|
-
--
|
|
1794
|
+
--subtree /private/library/another-project
|
|
1765
1795
|
```
|
|
1766
1796
|
|
|
1767
1797
|
Emission refuses an existing output directory. The module reads one JSON line
|
|
@@ -1848,8 +1878,8 @@ leaves the pending mounted report plus the local outcomes. Re-migration is
|
|
|
1848
1878
|
explicit unmount followed by mount.
|
|
1849
1879
|
|
|
1850
1880
|
```sh
|
|
1851
|
-
kit migrate unmount /rooms/work/library/project
|
|
1852
|
-
kit migrate unmount /rooms/work/library/project
|
|
1881
|
+
kit migrate unmount /rooms/work/library/project
|
|
1882
|
+
kit migrate unmount /rooms/work/library/project \
|
|
1853
1883
|
--confirm /rooms/work/library/project
|
|
1854
1884
|
```
|
|
1855
1885
|
|
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.22",
|
|
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.22/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.22/connect-linux.sh"
|
|
10
10
|
},
|
|
11
11
|
"workspace": {
|
|
12
12
|
"argument": "--workspace",
|
package/package.json
CHANGED
package/src/contract.js
CHANGED
|
@@ -297,7 +297,7 @@ export const EXPLICIT_SYNC_VERSION = "explicit-sync/2";
|
|
|
297
297
|
export const KIT_DOOR_UNMOUNT_PATH = "/kit/door/unmount";
|
|
298
298
|
|
|
299
299
|
export const KIT_WORKING_COPY_SELECTION_PATH = "/kit/working-copy/selection";
|
|
300
|
-
export const WORKING_COPY_VERSION = "working-copy-state/
|
|
300
|
+
export const WORKING_COPY_VERSION = "working-copy-state/3";
|
|
301
301
|
export const WORKING_COPIES_HOME = "working-copies";
|
|
302
302
|
export const WORKING_COPY_ROOM_HOMES = ["capture", "library", "collaboration"];
|
|
303
303
|
export const WORKING_COPY_PRIVATE_HOMES = ["library", "collaboration", "rooms", "agents", "house"];
|
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
|
@@ -19,7 +19,14 @@ import { localFiles } from "./local-files.js";
|
|
|
19
19
|
import { answerText, answerYaml } from "./mcp.js";
|
|
20
20
|
import { inheritedEnvironment } from "./command.js";
|
|
21
21
|
import { watchKitWork } from "./kit-work-stream.js";
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
applyWorkingCopies,
|
|
24
|
+
deliverWorkingCopies,
|
|
25
|
+
ownerCaller,
|
|
26
|
+
pushWorkingCopy,
|
|
27
|
+
recordIndexChange,
|
|
28
|
+
workingCopyPaths,
|
|
29
|
+
} from "./working-copy.js";
|
|
23
30
|
import { readConfiguration } from "./configuration.js";
|
|
24
31
|
import { launchBase, readyLaunchDirectory } from "./directory.js";
|
|
25
32
|
import {
|
|
@@ -863,7 +870,7 @@ export async function runWorkers(options, dependencies = {}) {
|
|
|
863
870
|
const { context, tmux } = await workerContext(options, dependencies);
|
|
864
871
|
const { session, runtime } = context;
|
|
865
872
|
const supervised = await maintainControl(session, async (signal) => {
|
|
866
|
-
await
|
|
873
|
+
await reportedWorkingCopies(applyWorkingCopies, context, signal);
|
|
867
874
|
return controlledPass(context, dependencies, context.configuration, signal);
|
|
868
875
|
}, session.signal);
|
|
869
876
|
return {
|
|
@@ -884,9 +891,8 @@ function reported(line) {
|
|
|
884
891
|
process.stderr.write(`kit: ${line}\n`);
|
|
885
892
|
}
|
|
886
893
|
|
|
887
|
-
async function
|
|
888
|
-
const
|
|
889
|
-
for (const refused of applied.refused) {
|
|
894
|
+
async function reportedWorkingCopies(act, context, signal) {
|
|
895
|
+
for (const refused of (await act(context, signal)).refused) {
|
|
890
896
|
reported(`working copy ${refused.path} refused: ${refused.code}`);
|
|
891
897
|
}
|
|
892
898
|
}
|
|
@@ -1078,10 +1084,12 @@ export async function runResident(options, dependencies = {}) {
|
|
|
1078
1084
|
const needsLogin = await queueAgentRefresh(context, dependencies, signal, undefined, true);
|
|
1079
1085
|
context.startLogins(needsLogin);
|
|
1080
1086
|
});
|
|
1081
|
-
const
|
|
1082
|
-
|
|
1083
|
-
(
|
|
1084
|
-
|
|
1087
|
+
const unavailable = (error) => reported(`working copies unavailable: ${error?.message ?? error}`);
|
|
1088
|
+
const copies = coalescedTask(signal, unavailable, () =>
|
|
1089
|
+
reportedWorkingCopies(applyWorkingCopies, context, signal),
|
|
1090
|
+
);
|
|
1091
|
+
const deliveries = coalescedTask(signal, unavailable, () =>
|
|
1092
|
+
reportedWorkingCopies(deliverWorkingCopies, context, signal),
|
|
1085
1093
|
);
|
|
1086
1094
|
const pointer = () => {
|
|
1087
1095
|
if (!update.requested) agentRefresh.trigger();
|
|
@@ -1114,18 +1122,20 @@ export async function runResident(options, dependencies = {}) {
|
|
|
1114
1122
|
if (sessions.length > 0) update.answer({ ready: false, sessions });
|
|
1115
1123
|
else if (
|
|
1116
1124
|
session.refused ||
|
|
1117
|
-
(!served.serving() && logins.size === 0 && agentRefresh.idle() && copies.idle())
|
|
1125
|
+
(!served.serving() && logins.size === 0 && agentRefresh.idle() && copies.idle() && deliveries.idle())
|
|
1118
1126
|
) {
|
|
1119
1127
|
return;
|
|
1120
1128
|
}
|
|
1121
1129
|
}
|
|
1122
1130
|
await wake.wait(interval, signal);
|
|
1131
|
+
if (!update.requested && !session.refused) deliveries.trigger();
|
|
1123
1132
|
}
|
|
1124
1133
|
} finally {
|
|
1125
1134
|
watcher.abort();
|
|
1126
1135
|
await watching;
|
|
1127
1136
|
await agentRefresh.settled();
|
|
1128
1137
|
await copies.settled();
|
|
1138
|
+
await deliveries.settled();
|
|
1129
1139
|
for (const running of logins.values()) running.controller.abort();
|
|
1130
1140
|
await Promise.all([...logins.values()].map((running) => running.task));
|
|
1131
1141
|
await context.agentRefresh;
|
package/src/working-copy.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { mkdir, readdir, realpath, rename, rm, stat } from "node:fs/promises";
|
|
2
|
+
import { mkdir, readdir, readFile, realpath, rename, rm, stat } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { readBindings } from "./binding.js";
|
|
5
5
|
import {
|
|
@@ -21,7 +21,7 @@ import { operationId } from "./operation.js";
|
|
|
21
21
|
import { formatCommandError, houseAnswered, houseFailed, KitRefusal } from "./refusal.js";
|
|
22
22
|
import { readNote, writeNote, writeText } from "./state.js";
|
|
23
23
|
import { bootstrapBundle, discoveryOf, door, withSession } from "./sync.js";
|
|
24
|
-
import { sidecarPath, structuredOrReplace } from "./sync-library.js";
|
|
24
|
+
import { isGeneratedProvenance, sidecarPath, structuredOrReplace } from "./sync-library.js";
|
|
25
25
|
import { locate, removeReplicaFile, writeReplicaFile } from "./sync-state.js";
|
|
26
26
|
|
|
27
27
|
const COMMITTER_NAME = "House Kit";
|
|
@@ -134,6 +134,7 @@ async function readScope(session, scope, signal) {
|
|
|
134
134
|
if (
|
|
135
135
|
authority === undefined ||
|
|
136
136
|
!Array.isArray(authority.protected_paths) ||
|
|
137
|
+
!Array.isArray(authority.operations?.write) ||
|
|
137
138
|
(scope.kind === "room" && view.manifest.anchor !== scope.ref)
|
|
138
139
|
) {
|
|
139
140
|
throw rejected("discover");
|
|
@@ -142,11 +143,16 @@ async function readScope(session, scope, signal) {
|
|
|
142
143
|
anchor: view.manifest.anchor,
|
|
143
144
|
position: view.manifest.position,
|
|
144
145
|
logEpoch: view.manifest.log_epoch,
|
|
145
|
-
protectedPaths: authority.protected_paths
|
|
146
|
+
protectedPaths: authority.protected_paths,
|
|
147
|
+
declared: declaration(authority),
|
|
146
148
|
files,
|
|
147
149
|
};
|
|
148
150
|
}
|
|
149
151
|
|
|
152
|
+
function declaration(authority) {
|
|
153
|
+
return { write: authority.operations.write, protected: authority.protected_paths };
|
|
154
|
+
}
|
|
155
|
+
|
|
150
156
|
function importData(text) {
|
|
151
157
|
const bytes = Buffer.from(text, "utf8");
|
|
152
158
|
return [Buffer.from(`data ${bytes.length}\n`), bytes, Buffer.from("\n")];
|
|
@@ -210,16 +216,28 @@ async function commitOf(repository, name, signal) {
|
|
|
210
216
|
return commit.toString("utf8").trim();
|
|
211
217
|
}
|
|
212
218
|
|
|
213
|
-
async function remembered(repository, revisions, commit, files, signal) {
|
|
219
|
+
async function remembered(repository, revisions, commit, files, signal, acknowledged = false) {
|
|
214
220
|
const tree = await treeOf(repository, commit, signal);
|
|
215
221
|
const next = { ...revisions };
|
|
216
222
|
for (const { path, revision } of files) {
|
|
217
223
|
const entry = tree.get(path);
|
|
218
|
-
if (entry
|
|
224
|
+
if (entry === undefined || revision === null) continue;
|
|
225
|
+
const seen = next[path]?.[entry.object] ?? [];
|
|
226
|
+
if (acknowledged || seen.at(-1)?.revision !== revision) {
|
|
227
|
+
next[path] = { ...next[path], [entry.object]: [...seen, { commit, revision }] };
|
|
228
|
+
}
|
|
219
229
|
}
|
|
220
230
|
return next;
|
|
221
231
|
}
|
|
222
232
|
|
|
233
|
+
async function revisionAt(copy, path, entry, base, signal) {
|
|
234
|
+
const seen = entry === undefined ? [] : (copy.revisions[path]?.[entry.object] ?? []);
|
|
235
|
+
for (const known of seen.toReversed()) {
|
|
236
|
+
if (seen.length === 1 || (await contains(copy.repository, known.commit, base, signal))) return known.revision;
|
|
237
|
+
}
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
240
|
+
|
|
223
241
|
function readOnlyContents(read) {
|
|
224
242
|
const contents = new Map();
|
|
225
243
|
for (const file of read.files) {
|
|
@@ -244,19 +262,22 @@ async function trackedPaths(repository, signal) {
|
|
|
244
262
|
return new Set(listed.toString("utf8").split("\0").filter((path) => path !== ""));
|
|
245
263
|
}
|
|
246
264
|
|
|
247
|
-
async function replaceReadOnly(repository,
|
|
265
|
+
async function replaceReadOnly(repository, held, written, removed, signal) {
|
|
248
266
|
const tracked = await trackedPaths(repository, signal);
|
|
249
|
-
const
|
|
250
|
-
for (const [path, content] of
|
|
267
|
+
const holding = new Set(held);
|
|
268
|
+
for (const [path, content] of written) {
|
|
251
269
|
if (tracked.has(path)) continue;
|
|
252
270
|
const target = locate(repository, "", path);
|
|
253
271
|
if (!held.has(path) && (await present(target))) continue;
|
|
254
272
|
await writeReplicaFile(target, content, signal);
|
|
273
|
+
holding.add(path);
|
|
255
274
|
}
|
|
256
|
-
for (const path of
|
|
257
|
-
if (
|
|
258
|
-
await removeReplicaFile(locate(repository, "", path));
|
|
275
|
+
for (const path of removed) {
|
|
276
|
+
if (!held.has(path)) continue;
|
|
277
|
+
if (!tracked.has(path)) await removeReplicaFile(locate(repository, "", path));
|
|
278
|
+
holding.delete(path);
|
|
259
279
|
}
|
|
280
|
+
return [...holding];
|
|
260
281
|
}
|
|
261
282
|
|
|
262
283
|
function covers(path, prefix) {
|
|
@@ -304,17 +325,16 @@ async function writeIgnoreRules(repository, read, signal) {
|
|
|
304
325
|
...[...house, ...granted].map((pattern) => `!${pattern}`),
|
|
305
326
|
"",
|
|
306
327
|
];
|
|
307
|
-
await
|
|
308
|
-
await
|
|
328
|
+
await ruled(join(repository, ".gitignore"), gitignore.join("\n"), signal);
|
|
329
|
+
await ruled(join(repository, ".ignore"), ignore.join("\n"), signal);
|
|
309
330
|
}
|
|
310
331
|
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
};
|
|
332
|
+
async function ruled(path, rules, signal) {
|
|
333
|
+
if ((await readFile(path, "utf8").catch(() => null)) !== rules) await writeText(path, rules, signal, 0o644);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function manifest(read, commit, readOnly) {
|
|
337
|
+
return { commit, position: read.position, log_epoch: read.logEpoch, read_only: readOnly, declared: read.declared };
|
|
318
338
|
}
|
|
319
339
|
|
|
320
340
|
function copyPath(directory, copy) {
|
|
@@ -376,6 +396,7 @@ async function materialize(context, scope, signal) {
|
|
|
376
396
|
const imported = received(scope, read, []);
|
|
377
397
|
let commit;
|
|
378
398
|
let revisions;
|
|
399
|
+
let readOnly;
|
|
379
400
|
try {
|
|
380
401
|
await runGit(staging, ["init", "-q", "-b", WORKING_COPY_BRANCH], { signal, failure: GIT_FAILED });
|
|
381
402
|
commit = (await importReceived(staging, imported, signal)).received;
|
|
@@ -385,7 +406,7 @@ async function materialize(context, scope, signal) {
|
|
|
385
406
|
failure: GIT_FAILED,
|
|
386
407
|
});
|
|
387
408
|
await runGit(staging, ["read-tree", "-m", "-u", "HEAD"], { signal, failure: GIT_FAILED });
|
|
388
|
-
await replaceReadOnly(staging,
|
|
409
|
+
readOnly = await replaceReadOnly(staging, new Set(), readOnlyContents(read), [], signal);
|
|
389
410
|
await writeIgnoreRules(staging, read, signal);
|
|
390
411
|
await rename(staging, repository);
|
|
391
412
|
} catch (error) {
|
|
@@ -399,7 +420,7 @@ async function materialize(context, scope, signal) {
|
|
|
399
420
|
room_handle: scope.handle,
|
|
400
421
|
repository,
|
|
401
422
|
active: true,
|
|
402
|
-
received: manifest(read, commit),
|
|
423
|
+
received: manifest(read, commit, readOnly),
|
|
403
424
|
revisions,
|
|
404
425
|
});
|
|
405
426
|
}
|
|
@@ -409,11 +430,14 @@ async function receive(context, copy, scope, signal) {
|
|
|
409
430
|
if (read.anchor !== copy.room_ref) throw rejected("bootstrap");
|
|
410
431
|
const imported = received(scope, read, [copy.received.commit]);
|
|
411
432
|
const commit = (await importReceived(copy.repository, imported, signal)).received;
|
|
412
|
-
|
|
433
|
+
const next = readOnlyContents(read);
|
|
434
|
+
const held = copy.received.read_only;
|
|
435
|
+
const removed = held.filter((path) => !next.has(path));
|
|
436
|
+
const readOnly = await replaceReadOnly(copy.repository, new Set(held), next, removed, signal);
|
|
413
437
|
await writeIgnoreRules(copy.repository, read, signal);
|
|
414
438
|
return {
|
|
415
439
|
...copy,
|
|
416
|
-
received: manifest(read, commit),
|
|
440
|
+
received: manifest(read, commit, readOnly),
|
|
417
441
|
revisions: await remembered(copy.repository, copy.revisions, commit, imported.files, signal),
|
|
418
442
|
};
|
|
419
443
|
}
|
|
@@ -432,6 +456,117 @@ function scopeRefused(error) {
|
|
|
432
456
|
return error instanceof KitRefusal && (houseAnswered(error) || error.code.startsWith("kit_working_copy_"));
|
|
433
457
|
}
|
|
434
458
|
|
|
459
|
+
function declaredWritable(path, authority) {
|
|
460
|
+
const deepest = (scopes) =>
|
|
461
|
+
Math.max(-1, ...scopes.filter((scope) => scope === "" || covers(path, scope)).map((scope) => scope.length));
|
|
462
|
+
return deepest(authority.operations.write) > deepest(authority.protected_paths);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
async function incorporated(copy, view, authority, signal) {
|
|
466
|
+
const tree = await treeOf(copy.repository, copy.received.commit, signal);
|
|
467
|
+
const held = new Set(copy.received.read_only);
|
|
468
|
+
const files = [];
|
|
469
|
+
const written = new Map();
|
|
470
|
+
const removed = [];
|
|
471
|
+
for (const change of view.changes) {
|
|
472
|
+
const gone = change.removed === true;
|
|
473
|
+
if (tree.has(change.path) || (!gone && !held.has(change.path) && declaredWritable(change.path, authority))) {
|
|
474
|
+
files.push({ path: change.path, revision: gone ? null : change.revision, content: gone ? null : change.content });
|
|
475
|
+
} else if (gone) {
|
|
476
|
+
removed.push(change.path, sidecarPath(change.path));
|
|
477
|
+
} else {
|
|
478
|
+
written.set(change.path, change.content);
|
|
479
|
+
if (typeof change.provenance === "string") written.set(sidecarPath(change.path), change.provenance);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
const message = `House ${housePath(scopeOf(copy))} at position ${view.position}`;
|
|
483
|
+
const commit =
|
|
484
|
+
files.length === 0
|
|
485
|
+
? copy.received.commit
|
|
486
|
+
: (await importReceived(copy.repository, { message, parents: [copy.received.commit], files }, signal)).received;
|
|
487
|
+
const readOnly = await replaceReadOnly(copy.repository, held, written, removed, signal);
|
|
488
|
+
if (view.changes.length > 0) {
|
|
489
|
+
const listed = [
|
|
490
|
+
...[...(await treeOf(copy.repository, commit, signal)).keys()].map((path) => ({ path, writable: true })),
|
|
491
|
+
...readOnly.filter((path) => !isGeneratedProvenance(path)).map((path) => ({ path, writable: false })),
|
|
492
|
+
].sort((left, right) => (left.path < right.path ? -1 : 1));
|
|
493
|
+
await writeIgnoreRules(copy.repository, { files: listed, protectedPaths: authority.protected_paths }, signal);
|
|
494
|
+
}
|
|
495
|
+
return {
|
|
496
|
+
...copy,
|
|
497
|
+
received: { ...copy.received, commit, position: view.position, log_epoch: view.log_epoch, read_only: readOnly },
|
|
498
|
+
revisions: await remembered(copy.repository, copy.revisions, commit, files, signal),
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
async function deliver(context, path, authority, signal) {
|
|
503
|
+
if (
|
|
504
|
+
typeof authority.position !== "string" ||
|
|
505
|
+
typeof authority.log_epoch !== "string" ||
|
|
506
|
+
!Array.isArray(authority.protected_paths) ||
|
|
507
|
+
!Array.isArray(authority.operations?.write)
|
|
508
|
+
) {
|
|
509
|
+
throw rejected("discover");
|
|
510
|
+
}
|
|
511
|
+
let copy = await readCopy(path);
|
|
512
|
+
if (copy?.active && JSON.stringify(copy.received.declared) !== JSON.stringify(declaration(authority))) {
|
|
513
|
+
await writeCopy(context.directory, await receive(context, copy, scopeOf(copy), signal), signal);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
while (
|
|
517
|
+
copy?.active &&
|
|
518
|
+
(copy.received.log_epoch !== authority.log_epoch || BigInt(copy.received.position) < BigInt(authority.position))
|
|
519
|
+
) {
|
|
520
|
+
const since = { position: copy.received.position, log_epoch: copy.received.log_epoch };
|
|
521
|
+
let view;
|
|
522
|
+
try {
|
|
523
|
+
view = await enumerated(context, copy, since, signal);
|
|
524
|
+
} catch (error) {
|
|
525
|
+
if (error.code === "replica_behind") return;
|
|
526
|
+
if (error.code !== "position_expired") throw error;
|
|
527
|
+
await writeCopy(context.directory, await receive(context, copy, scopeOf(copy), signal), signal);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
if (view.position === since.position) return;
|
|
531
|
+
copy = await incorporated(copy, view, authority, signal);
|
|
532
|
+
await writeCopy(context.directory, copy, signal);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async function delivering(context, signal) {
|
|
537
|
+
const copies = (await heldCopies(context.directory)).filter((copy) => copy.active);
|
|
538
|
+
if (copies.length === 0) return { refused: [], lost: false };
|
|
539
|
+
const prefixes = copies.flatMap((copy) => selectedPrefixes(scopeOf(copy)));
|
|
540
|
+
const discovery = discoveryOf(await door(context.session, KIT_DOOR_DISCOVER_PATH, { prefixes }, signal));
|
|
541
|
+
const refused = [];
|
|
542
|
+
let lost = false;
|
|
543
|
+
for (const copy of copies) {
|
|
544
|
+
const at = housePath(scopeOf(copy));
|
|
545
|
+
const authority = discovery.authorities.find((entry) => entry?.room_ref === copy.room_ref);
|
|
546
|
+
if (authority === undefined) {
|
|
547
|
+
lost = true;
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
if (authority.managed_replication !== "admitted") {
|
|
551
|
+
refused.push({ path: at, code: "operation_denied" });
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
const path = copyPath(context.directory, copy);
|
|
555
|
+
try {
|
|
556
|
+
await exclusively(path, () => deliver(context, path, authority, signal));
|
|
557
|
+
} catch (error) {
|
|
558
|
+
if (!scopeRefused(error)) throw error;
|
|
559
|
+
refused.push({ path: at, code: error.code });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return { refused, lost };
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
export async function deliverWorkingCopies(context, signal) {
|
|
566
|
+
const delivered = await delivering(context, signal);
|
|
567
|
+
return delivered.lost ? applyWorkingCopies(context, signal) : { refused: delivered.refused };
|
|
568
|
+
}
|
|
569
|
+
|
|
435
570
|
export async function applyWorkingCopies(context, signal) {
|
|
436
571
|
const selected = (await savedSelection(context.session, signal)).filter(
|
|
437
572
|
(scope) => scope.kind === "private" || scope.handle !== null,
|
|
@@ -461,7 +596,7 @@ export async function applyWorkingCopies(context, signal) {
|
|
|
461
596
|
refused.push({ path: housePath(scope), code: error.code });
|
|
462
597
|
}
|
|
463
598
|
}
|
|
464
|
-
return { refused };
|
|
599
|
+
return { refused: [...refused, ...(await delivering(context, signal)).refused] };
|
|
465
600
|
}
|
|
466
601
|
|
|
467
602
|
export async function workingCopyPaths(directory) {
|
|
@@ -694,8 +829,8 @@ function renamed(chains, reached, before, after) {
|
|
|
694
829
|
return new Map(pairs.filter(([from, to]) => pairs.filter((pair) => pair[0] === from || pair[1] === to).length === 1));
|
|
695
830
|
}
|
|
696
831
|
|
|
697
|
-
async function relocated(copy, root, [from, to], prior, next, signal) {
|
|
698
|
-
const base = copy
|
|
832
|
+
async function relocated(copy, root, [from, to], [prior, next], house, signal) {
|
|
833
|
+
const base = await revisionAt(copy, from, prior, house, signal);
|
|
699
834
|
const rename = { op: "rename", path: `${root}/${from}`, to: `${root}/${to}`, base };
|
|
700
835
|
if (prior.object === next.object) return [rename];
|
|
701
836
|
return [rename, { op: "replace", path: `${root}/${to}`, base, content: await blobText(copy.repository, next, to, signal) }];
|
|
@@ -713,13 +848,13 @@ async function derived(copy, base, commit, signal) {
|
|
|
713
848
|
const prior = before.get(path);
|
|
714
849
|
if (moved.has(path)) {
|
|
715
850
|
const to = moved.get(path);
|
|
716
|
-
changes.push(...(await relocated(copy, root, [path, to], prior, after.get(to), signal)));
|
|
851
|
+
changes.push(...(await relocated(copy, root, [path, to], [prior, after.get(to)], base, signal)));
|
|
717
852
|
continue;
|
|
718
853
|
}
|
|
719
854
|
const next = after.get(path);
|
|
720
855
|
if (prior?.object === next?.object) continue;
|
|
721
856
|
const target = `${root}/${path}`;
|
|
722
|
-
const revision = copy
|
|
857
|
+
const revision = await revisionAt(copy, path, prior, base, signal);
|
|
723
858
|
if (next === undefined) {
|
|
724
859
|
changes.push({ op: "remove", path: target, base: revision });
|
|
725
860
|
continue;
|
|
@@ -761,8 +896,15 @@ async function recorded(copy, files, message, acknowledging, signal) {
|
|
|
761
896
|
return {
|
|
762
897
|
copy: {
|
|
763
898
|
...copy,
|
|
764
|
-
received: { ...copy.received, commit: imported.received },
|
|
765
|
-
revisions: await remembered(
|
|
899
|
+
received: { ...copy.received, commit: imported.received, ...(acknowledging === null ? {} : copy.push.from) },
|
|
900
|
+
revisions: await remembered(
|
|
901
|
+
copy.repository,
|
|
902
|
+
copy.revisions,
|
|
903
|
+
imported.acknowledged ?? imported.received,
|
|
904
|
+
files,
|
|
905
|
+
signal,
|
|
906
|
+
imported.acknowledged !== null,
|
|
907
|
+
),
|
|
766
908
|
},
|
|
767
909
|
acknowledged: imported.acknowledged,
|
|
768
910
|
};
|
|
@@ -952,11 +1094,12 @@ async function expired(context, copy, text, signal) {
|
|
|
952
1094
|
const entry = house.get(path);
|
|
953
1095
|
written.push({
|
|
954
1096
|
path: `${root}/${path}`,
|
|
955
|
-
revision: entry === undefined ? null : confirmed
|
|
1097
|
+
revision: entry === undefined ? null : await revisionAt(confirmed, path, entry, confirmed.received.commit, signal),
|
|
956
1098
|
content: await blobText(copy.repository, entry, path, signal),
|
|
957
1099
|
});
|
|
958
1100
|
}
|
|
959
|
-
const
|
|
1101
|
+
const from = { position: confirmed.received.position, log_epoch: confirmed.received.log_epoch };
|
|
1102
|
+
const accepted = { ...confirmed, push: { ...push, state: "accepted", written, from } };
|
|
960
1103
|
await writeCopy(context.directory, accepted, signal);
|
|
961
1104
|
return finish(context, accepted, signal);
|
|
962
1105
|
}
|
|
@@ -1030,6 +1173,7 @@ async function pushed(context, path, at, selector, caller, signal) {
|
|
|
1030
1173
|
operation_id: operationId(),
|
|
1031
1174
|
changes,
|
|
1032
1175
|
commit,
|
|
1176
|
+
from: { position: copy.received.position, log_epoch: copy.received.log_epoch },
|
|
1033
1177
|
worktree: at.worktree,
|
|
1034
1178
|
branch,
|
|
1035
1179
|
...(since === null ? {} : { since }),
|