@agentshouse/kit 0.1.0-alpha.2 → 0.1.0-alpha.21
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 +299 -26
- package/bin/connect-linux.sh +85 -13
- package/bin/kit.js +23 -3
- package/bootstrap.json +17 -0
- package/package.json +7 -2
- package/src/adapter-mcp.js +0 -13
- package/src/agents.js +63 -8
- package/src/binding.js +41 -0
- package/src/bridge.js +1 -1
- package/src/contract.js +21 -3
- package/src/git-main.js +138 -0
- package/src/git.js +62 -0
- package/src/host.js +6 -2
- package/src/house-main.js +65 -8
- package/src/house.js +345 -73
- package/src/inbound.js +2 -18
- package/src/kit-work-stream.js +88 -0
- package/src/launcher.js +9 -3
- package/src/local-files.js +3 -11
- package/src/login.js +2 -1
- package/src/mcp.js +13 -0
- package/src/mount.js +13 -21
- package/src/repository.js +3 -61
- package/src/session-bin/git +2 -0
- package/src/setup.js +7 -2
- package/src/source.js +44 -30
- package/src/sync-library.js +7 -11
- package/src/sync-state.js +4 -0
- package/src/sync.js +15 -10
- package/src/telegram.js +29 -11
- package/src/worker.js +529 -116
- package/src/working-copy.js +1210 -0
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.21/connect-linux.sh)
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
The release asset pins one immutable multi-architecture image digest. It creates
|
|
@@ -20,12 +20,99 @@ and starts the container with Docker's `unless-stopped` lifecycle. Pass
|
|
|
20
20
|
`--workspace /absolute/path` for one other workspace root or `--manual` for
|
|
21
21
|
the existing URL and one-time-code login route. `--house <origin>` selects
|
|
22
22
|
another House and is retained in Kit home. A repeated invocation reuses the
|
|
23
|
-
same container, credential and Environment
|
|
24
|
-
|
|
23
|
+
same container, credential and Environment.
|
|
24
|
+
|
|
25
|
+
Running a newer release's bootstrap the same way is the only update. When the
|
|
26
|
+
installed container runs another image, the bootstrap pulls this release's
|
|
27
|
+
image and asks the running resident Kit to drain with `kit resident drain`. The
|
|
28
|
+
resident stops acquiring Runs, resumptions, setup and agent CLI work, while the
|
|
29
|
+
Sessions it supervises keep their renewal, owner stops, answers and reports.
|
|
30
|
+
While any Agent Session is still running or unreported, including one whose Run
|
|
31
|
+
was being acquired when the drain began, the bootstrap names it and leaves the
|
|
32
|
+
container as it is; rerun it once they end. Once nothing remains,
|
|
33
|
+
the resident stops renewing, releases its exact lease and stays quiescent, and
|
|
34
|
+
the bootstrap replaces the container with one on this release's image over the
|
|
35
|
+
same Kit home and workspace. The credential, Environment, Kit-home state and
|
|
36
|
+
everything under the workspace are kept; anything installed elsewhere in the old
|
|
37
|
+
container is not. A stopped container is replaced without a drain.
|
|
38
|
+
|
|
39
|
+
If House refuses the installed Kit with `kit_upgrade_required`, including in
|
|
40
|
+
its answer to a command the resident serves, the resident calls House no more,
|
|
41
|
+
neither to release nor to report. It keeps supervising
|
|
42
|
+
every running Session locally and never stops one to make progress. A Session
|
|
43
|
+
that ends keeps its exact binding, stop observation and outcome in Kit home,
|
|
44
|
+
and the drain answers ready once no supervised process runs. The updated Kit
|
|
45
|
+
waits for the old lease to expire, acquires the next one and reports each held
|
|
46
|
+
Session before it acquires any work, restoring no process and inventing no
|
|
47
|
+
outcome.
|
|
48
|
+
|
|
49
|
+
The versioned `bootstrap.json` is the machine-readable command source for this
|
|
50
|
+
release. It is exported from the npm package as `@agentshouse/kit/bootstrap`
|
|
51
|
+
(JSON import) and published byte-for-byte beside `connect-linux.sh` in the
|
|
52
|
+
GitHub release. Its schema version is `1`, its `version` is the Kit package
|
|
53
|
+
version, and `hosts` contains only bootstrap scripts published in that release.
|
|
54
|
+
The package exports only the `./bootstrap` subpath; `./package.json` and source
|
|
55
|
+
subpaths are not exported. For this release, only `hosts.linux` exists. In this
|
|
56
|
+
schema example, `<version>` stands for the package's version:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"schemaVersion": 1,
|
|
61
|
+
"version": "<version>",
|
|
62
|
+
"hosts": {
|
|
63
|
+
"linux": {
|
|
64
|
+
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v<version>/connect-linux.sh)",
|
|
65
|
+
"asset": {
|
|
66
|
+
"name": "connect-linux.sh",
|
|
67
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v<version>/connect-linux.sh"
|
|
68
|
+
},
|
|
69
|
+
"workspace": { "argument": "--workspace", "quoting": "posix" }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`command` is complete for the default workspace. To show a selected absolute
|
|
76
|
+
workspace, append the declared `workspace.argument` and the path as one POSIX
|
|
77
|
+
shell-quoted argument. For example, `/home/user/My Agents' House` becomes
|
|
78
|
+
`--workspace '/home/user/My Agents'\'' House'`. Quote the entire path before
|
|
79
|
+
adding it to a copyable shell command; never interpolate it as shell syntax.
|
|
80
|
+
The bootstrap validates an absolute workspace root and encodes it as one Docker
|
|
81
|
+
`--mount` CSV source field, including commas and double quotes in the name.
|
|
82
|
+
The package manifest contains no image digest: the release workflow
|
|
83
|
+
fills the immutable image digest into the published script after the package
|
|
84
|
+
has been built.
|
|
25
85
|
|
|
26
86
|
The bootstrap does not install native Kit on an ordinary computer. The native
|
|
27
87
|
package remains for managed Nodes and an explicitly selected eligible Kit host.
|
|
28
88
|
|
|
89
|
+
The same bootstrap is the host's entry point for the existing `kit` commands:
|
|
90
|
+
append `kit` and the command to the connect command above, as in
|
|
91
|
+
`bash <(curl -fsSL …/connect-linux.sh) kit apply` or
|
|
92
|
+
`bash <(curl -fsSL …/connect-linux.sh) kit telegram connect`. It forwards the
|
|
93
|
+
command and its arguments unchanged into the installed container through the
|
|
94
|
+
host's Docker client and ends with the command's own output and exit status.
|
|
95
|
+
Standard input stays attached and a terminal stays a terminal, so a hidden
|
|
96
|
+
prompt such as the Telegram bot token is read inside the container and never
|
|
97
|
+
becomes an argument or printed output. With the output redirected, the
|
|
98
|
+
command's output and errors stay separate and the terminal stops echoing typed
|
|
99
|
+
input until the command ends. A running container runs the command beside its
|
|
100
|
+
resident Kit; a stopped one runs it in a one-off container over the same Kit
|
|
101
|
+
home and workspace and stays stopped. `kit login` takes the bootstrap's own
|
|
102
|
+
login route, a one-off container on the host network so the host browser can
|
|
103
|
+
return to its loopback listener, or `--manual` for the URL and one-time code.
|
|
104
|
+
Forwarding installs no host-native Kit and gives the container no Docker socket
|
|
105
|
+
or further mount; it refuses when no Kit is installed or the installed one
|
|
106
|
+
differs from this bootstrap's.
|
|
107
|
+
|
|
108
|
+
The owner's working copy push is forwarded the same way: run
|
|
109
|
+
`bash <(curl -fsSL …/connect-linux.sh) house git push [commit] --owner` from a
|
|
110
|
+
directory beneath the workspace root, such as a Room copy under
|
|
111
|
+
`working-copies/rooms/`. The bootstrap runs the container's `house` in the
|
|
112
|
+
matching directory beneath `/agents/house`, through the resident's lease when
|
|
113
|
+
the container runs, and refuses a directory outside the workspace before
|
|
114
|
+
anything reaches House.
|
|
115
|
+
|
|
29
116
|
## Building the package
|
|
30
117
|
|
|
31
118
|
The repository checkout is the development source. Build the package
|
|
@@ -48,7 +135,8 @@ Runtime dependencies stay minimal and exactly pinned in `package.json` and
|
|
|
48
135
|
`package-lock.json`: House's own `@agentshouse/mdmodel`, `yaml`, which reads
|
|
49
136
|
House's text answers, and the Agent Client Protocol SDK
|
|
50
137
|
`@agentclientprotocol/sdk` with its peer `zod`, which is this Kit's one protocol
|
|
51
|
-
client for the compiled adapter contracts
|
|
138
|
+
client for the compiled adapter contracts, and `ws` for the authenticated Kit
|
|
139
|
+
control stream. The SDK's approval
|
|
52
140
|
receipt is [`docs/agents/acp-sdk-dependency-approval.json`](docs/agents/acp-sdk-dependency-approval.json),
|
|
53
141
|
which the User wrote once this adoption's proof had passed. The adapter
|
|
54
142
|
packages themselves are Environment prerequisites, not Kit dependencies, and
|
|
@@ -575,6 +663,17 @@ minimum's major - under
|
|
|
575
663
|
`$HOUSE_KIT_HOME/agents/<kind>/releases/<release>/`, never touching what the
|
|
576
664
|
owner installed globally, and an acquired route's command is that installed
|
|
577
665
|
executable rather than a path anyone writes into `kit.json`.
|
|
666
|
+
The resident Kit also listens for an `agents` availability pointer on its
|
|
667
|
+
control stream. It reads the whole wanted list from House over HTTPS, removes
|
|
668
|
+
Kit-owned CLI directories that are no longer wanted, installs newly wanted
|
|
669
|
+
kinds, and reports its held state immediately, including while another Agent
|
|
670
|
+
run remains active. A newly wanted kind that needs provider authorization uses
|
|
671
|
+
the existing guided House login ceremony; Kit reports readiness again when it
|
|
672
|
+
ends. A reconnect reads the list again, so a pointer missed during a stream
|
|
673
|
+
outage changes no authority.
|
|
674
|
+
Removal waits for an active worker pass or any unresolved live WorkerSession
|
|
675
|
+
binding to stop using a CLI directory; the Kit keeps reporting that kind until
|
|
676
|
+
it removes the directory.
|
|
578
677
|
|
|
579
678
|
Every candidate release is proved before it serves: the Kit installs it beside
|
|
580
679
|
the current release, runs the contract's own conformance check - the protocol's
|
|
@@ -669,11 +768,13 @@ prompt's stop reason through the contract's own table to one of four turn
|
|
|
669
768
|
outcomes: completed, interrupted, refused by the provider, or exhausted. A stop
|
|
670
769
|
reason the contract does not name, including a vendor-specific one a different
|
|
671
770
|
contract names, fails the turn as `kit_adapter_contract_mismatch` rather than
|
|
672
|
-
being guessed.
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
771
|
+
being guessed. After committing the Run, and again on every resumption, the
|
|
772
|
+
Kit reads what the Session was started for from House's
|
|
773
|
+
`/kit/runs/<run>/request` with the Session credential: the Routine Request with
|
|
774
|
+
its input, references, requester and the pinned revision's instruction, or the
|
|
775
|
+
sealed conversation turn. It sends that as the first prompt block, followed by
|
|
776
|
+
current House guidance fetched through the Run connection, so the Agent calls
|
|
777
|
+
nothing to learn it. An unreadable request or guidance refuses the turn. It forwards only adapter-classified User-visible assistant
|
|
677
778
|
text back over the local channel, and leaves reasoning, tool traces, and
|
|
678
779
|
terminal output where they are. The supervised process's own House calls travel
|
|
679
780
|
that same channel through the `house` CLI. The Kit holds the WorkerRunToken, replaces it in place when
|
|
@@ -736,7 +837,9 @@ nonzero exit status. The CLI holds
|
|
|
736
837
|
no credential: it calls over its Session's bridge, which attaches the Session
|
|
737
838
|
credential and refuses a caller without that Session's nonce as
|
|
738
839
|
`kit_bridge_unauthorized`. House network calls use only the selected
|
|
739
|
-
`2026-07-28` profile.
|
|
840
|
+
`2026-07-28` profile. `house git push` is the one local verb House does not list;
|
|
841
|
+
[working copies](#working-copies) describe it, and `house --help` and
|
|
842
|
+
`house git push --help` explain it.
|
|
740
843
|
|
|
741
844
|
Two verbs name files on the Session's own computer: `upload_attachment` takes
|
|
742
845
|
one `path`, and `append_record` takes `attachments` as local paths. The CLI
|
|
@@ -819,8 +922,8 @@ receiving a complete byte stream, validating any reported size, and writing the
|
|
|
819
922
|
file. A lost acknowledgement preserves the completed local file for House's
|
|
820
923
|
idempotent admission and later cleanup. Failed files remain pending; House's
|
|
821
924
|
all-files-ready gate keeps their turn unprocessed.
|
|
822
|
-
The provider receives absolute local paths beside the owning messages
|
|
823
|
-
|
|
925
|
+
The provider receives absolute local paths beside the owning messages in its
|
|
926
|
+
first prompt. The cache persists across
|
|
824
927
|
turns; only House-authorized terminal Run cleanup removes its Run directory.
|
|
825
928
|
|
|
826
929
|
The accepted attachment's retained original is separate from that cache. The Kit
|
|
@@ -845,8 +948,8 @@ outcome it handled, and exits non-zero while any of them is unsaved.
|
|
|
845
948
|
One `kit worker run` then takes one House-authorized resumption, or otherwise
|
|
846
949
|
acquires one waiting Run, and supervises exactly that WorkerSession. A resumed
|
|
847
950
|
conversation loads its retained native provider session through ACP
|
|
848
|
-
`session/load`, receives the current input
|
|
849
|
-
|
|
951
|
+
`session/load`, receives the current input House answers for the Run in its
|
|
952
|
+
first prompt, and executes one turn. Historical output replayed during
|
|
850
953
|
loading is not emitted as new assistant output. Missing or rejected provider
|
|
851
954
|
continuation never starts a replacement provider conversation in the same Run.
|
|
852
955
|
|
|
@@ -913,7 +1016,12 @@ content, no prompt, and no resolved secret, and Session authority is always
|
|
|
913
1016
|
reissued by House rather than recovered from disk. It is evidence for
|
|
914
1017
|
reconciliation, never permission to resume or recreate work, and nothing keeps
|
|
915
1018
|
it once House has answered for the Run: a settled terminal state, a confirmed
|
|
916
|
-
owner stop, and a retained Provider continuation each drop it.
|
|
1019
|
+
owner stop, and a retained Provider continuation each drop it. When the
|
|
1020
|
+
supervised process ends, the binding first records what the Kit observed: the
|
|
1021
|
+
sealed final text, whether the turn completed, the capacity or provider refusal,
|
|
1022
|
+
the execution failure and whether a Provider continuation is retained. The Kit
|
|
1023
|
+
then reports that stop, and a restarted Kit reports the same recorded stop
|
|
1024
|
+
instead of treating the session as lost.
|
|
917
1025
|
|
|
918
1026
|
A restarted `kit worker run` reconciles the records it kept before it acquires
|
|
919
1027
|
anything new. It reports every retained settlement note first, because those
|
|
@@ -1062,10 +1170,27 @@ retained references still waiting to be saved.
|
|
|
1062
1170
|
Every command that changes or reads installation state takes this
|
|
1063
1171
|
installation's exclusive Environment control lease for the length of its run
|
|
1064
1172
|
and renews it before it expires, so two Kit commands never work against the
|
|
1065
|
-
same installation at once. A command that ends, or is interrupted
|
|
1066
|
-
has stopped
|
|
1067
|
-
|
|
1068
|
-
|
|
1173
|
+
same installation at once. A command that ends, is refused, or is interrupted
|
|
1174
|
+
once its work has stopped releases that exact lease so the next command acquires
|
|
1175
|
+
at once. A `kit login` credential is presented only to take that lease.
|
|
1176
|
+
|
|
1177
|
+
While `kit resident` holds that lease, a command runs through it instead: the
|
|
1178
|
+
command presents no credential, the resident confirms its current lease with
|
|
1179
|
+
House and sends the command's House requests under it, an interrupted command
|
|
1180
|
+
cancels its request there, and the lease stays held when the command ends. The
|
|
1181
|
+
resident serves one such command at a time and refuses another with
|
|
1182
|
+
`environment_control_lease_active`, so two commands still never work at once.
|
|
1183
|
+
A resident whose lease House refuses answers the command with that refusal; the
|
|
1184
|
+
command never acquires a lease of its own while a resident serves it. While an
|
|
1185
|
+
update drains, `state/update-drain` refuses every such command, `kit login` and
|
|
1186
|
+
`kit worker run` with `kit_update_draining` before it enrolls, acquires or
|
|
1187
|
+
delegates anything. A command already served keeps the resident's lease until
|
|
1188
|
+
it ends, while the resident refuses its acquisition of new work and its
|
|
1189
|
+
Telegram updates with `kit_update_draining`, which ends a served
|
|
1190
|
+
`kit telegram serve` once each of its activities reports the deliveries and
|
|
1191
|
+
files it already acquired; until it ends, the drain answers that it waits for
|
|
1192
|
+
that command and the bootstrap leaves the container as it is. The bootstrap
|
|
1193
|
+
removes the file when it replaces the container.
|
|
1069
1194
|
|
|
1070
1195
|
Interrupting the command cancels the running connector without advancing the
|
|
1071
1196
|
checkpoint.
|
|
@@ -1104,15 +1229,161 @@ interrupted run is never resumed: the next run opens a new manifest and House
|
|
|
1104
1229
|
answers with what is still needed. The command reports the Publication
|
|
1105
1230
|
reference, the paths delivered, the items refused and the ending.
|
|
1106
1231
|
|
|
1232
|
+
## Working copies
|
|
1233
|
+
|
|
1234
|
+
The Environment owner chooses in House which Rooms this Environment keeps
|
|
1235
|
+
locally, and whether it keeps `/private`. An Environment whose owner never chose
|
|
1236
|
+
copies nothing, and a Room joined later stays uncopied until the owner selects
|
|
1237
|
+
it. The resident Kit reads the saved choice through
|
|
1238
|
+
`POST /kit/working-copy/selection` when it starts, whenever its control stream
|
|
1239
|
+
connects or reconnects, and whenever House points it there after a save;
|
|
1240
|
+
`kit worker run` reads it once before its pass.
|
|
1241
|
+
|
|
1242
|
+
Each selected Room becomes one ordinary Git repository at
|
|
1243
|
+
`/agents/house/working-copies/rooms/<room_ref>`, and `/private` one at
|
|
1244
|
+
`/agents/house/working-copies/private`. The Room reference names the location,
|
|
1245
|
+
so a Room handle change moves nothing. Every Agent launched afterwards receives
|
|
1246
|
+
the House path and local repository of each active copy in its context, one
|
|
1247
|
+
line each, such as `/rooms/work: /agents/house/working-copies/rooms/r_1x.q`.
|
|
1248
|
+
|
|
1249
|
+
A Room repository holds the Room's complete readable content: `ROOM.md`,
|
|
1250
|
+
`library/`, `capture/` with its provenance sidecars, and `collaboration/`. The
|
|
1251
|
+
`/private` repository holds its Library, collaboration, each Room's private
|
|
1252
|
+
`AGENTS.md`, the `agents/` cards and `house/`. Attached settings and attachment
|
|
1253
|
+
bytes are not copied. The branch `main` starts at the commit House served, which
|
|
1254
|
+
tracks exactly the files House declares writable under the Environment's
|
|
1255
|
+
current grant. Every other file is read-only House content: the generated
|
|
1256
|
+
`.gitignore` names and explains it, so Git never tracks it, and the matching
|
|
1257
|
+
`.ignore` keeps it visible to ripgrep and other native search. Neither file
|
|
1258
|
+
grants anything, and House refuses a change to a read-only path however it is
|
|
1259
|
+
committed.
|
|
1260
|
+
|
|
1261
|
+
Local edits, staging and commits change nothing in House until a commit is
|
|
1262
|
+
pushed. `refs/house/received` records the House content last received; each
|
|
1263
|
+
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 and the House state that received it, stays in Kit home under
|
|
1266
|
+
`state/working-copies/`, outside the repository. A local commit is a local tree, never a House revision.
|
|
1267
|
+
|
|
1268
|
+
The resident Kit keeps every active copy current. On each pass, and whenever
|
|
1269
|
+
it reads the saved choice, one `POST /kit/door/discover` tells it where each
|
|
1270
|
+
copied Room stands, and for a Room that moved on it reads the changes after the
|
|
1271
|
+
copy's recorded position through `POST /kit/door/enumerate`. A changed, new or
|
|
1272
|
+
removed authored file becomes a commit on `refs/house/received`, its House
|
|
1273
|
+
revision kept in Kit home; changed, new or removed read-only content, such as
|
|
1274
|
+
Capture with its provenance sidecar or a House projection, is written into or
|
|
1275
|
+
removed from the ignored local tree, and `.gitignore` and `.ignore` follow it.
|
|
1276
|
+
Delivery never touches the branch, the index or a tracked file, and a push
|
|
1277
|
+
waiting for its outcome or its local integration keeps its record. A push takes
|
|
1278
|
+
each file's revision from the newest House state in its own base's history that
|
|
1279
|
+
received that content, so a local commit keeps the base it was made on even when
|
|
1280
|
+
House returns to the same text under a newer revision. A file new to the copy is tracked when a write scope
|
|
1281
|
+
House declares for the Room covers it more closely than a declared protected
|
|
1282
|
+
path, and a local file already sitting where new read-only content arrives
|
|
1283
|
+
stays yours: Kit neither overwrites nor later removes it. When the write scopes
|
|
1284
|
+
or protected paths House declares for the Room change, Kit reads the Room again
|
|
1285
|
+
as a reselection does. A position older than House
|
|
1286
|
+
retains, or from before House's database was restored, makes Kit read the Room
|
|
1287
|
+
again as a reselection does; while House's replica has not reached the position
|
|
1288
|
+
the copy waits for the next pass. A Room House no longer answers under its
|
|
1289
|
+
recorded handle makes Kit read the saved choice again, which follows a handle
|
|
1290
|
+
change or marks the copy inactive, and a Room that lost sync is named on
|
|
1291
|
+
standard error as `operation_denied` and keeps what it holds. `kit worker run`
|
|
1292
|
+
delivers once, after reading the saved choice.
|
|
1293
|
+
|
|
1294
|
+
`house git push [commit]`, run anywhere inside a copy or one of its linked
|
|
1295
|
+
worktrees, submits one commit, `HEAD` by default, resolved once. Kit compares
|
|
1296
|
+
that commit's tree with the newest House state in its history, so every linked
|
|
1297
|
+
worktree keeps its own base, and sends the difference as one ordinary House edit:
|
|
1298
|
+
a changed field, section or preamble against its own prior value, any other
|
|
1299
|
+
change as the whole file against its exact revision, a new file as a create and
|
|
1300
|
+
a committed deletion as a removal. Staged and unstaged work, and files absent
|
|
1301
|
+
only from the working tree, are never sent. Native `git push` reaches no House
|
|
1302
|
+
remote. Inside an Agent Session the push goes through that Session's bridge,
|
|
1303
|
+
under its own authority and provenance; outside one it needs
|
|
1304
|
+
`house git push [commit] --owner`, which the resident Kit carries out under its
|
|
1305
|
+
control lease when a resident runs, and which otherwise acquires and releases
|
|
1306
|
+
its own. A Session refuses `--owner`, and a missing bridge is never replaced by
|
|
1307
|
+
owner authority.
|
|
1308
|
+
|
|
1309
|
+
Inside an Agent Session `git` is Kit's shim in front of the installed Git, and
|
|
1310
|
+
every command runs exactly as that Git runs it. In a copy or one of its linked
|
|
1311
|
+
worktrees the shim compares that worktree's index before and after each
|
|
1312
|
+
command. When `git mv` succeeds, Git's own dry run of the same command names
|
|
1313
|
+
which path went where, and the shim hands each move to the resident Kit, which
|
|
1314
|
+
keeps every moved document's successive paths in Kit home. A staged move
|
|
1315
|
+
belongs to its worktree's index: a command there that takes its path out of the
|
|
1316
|
+
index or brings it in anew, such as `git reset`, `git restore --staged`,
|
|
1317
|
+
`git rm`, `git stash` or `git switch`, takes it back, and so does removing the
|
|
1318
|
+
worktree. A `git commit` whose commit carries the move ties it to that commit.
|
|
1319
|
+
A tied move counts for every pushed commit whose history holds one of its
|
|
1320
|
+
commits, whatever the index does meanwhile. `git commit --amend` and
|
|
1321
|
+
`git rebase`, Kit's own rebase onto House's result included, also tie it to
|
|
1322
|
+
their result when the rewritten branch still carries it, so aborting the rebase
|
|
1323
|
+
or resetting back to the original commit keeps it as well. A `git reset` that
|
|
1324
|
+
leaves every commit of the move behind without touching its latest path in the
|
|
1325
|
+
index returns it to that worktree's index, where the next commit ties it again.
|
|
1326
|
+
Any other rewrite, such as a cherry-pick, leaves the move tied to its earlier
|
|
1327
|
+
commits. A push sends House's rename of the same document from the path its
|
|
1328
|
+
House base holds to the latest of its counted paths the pushed commit holds,
|
|
1329
|
+
when the commit no longer has the first and the base did not have the second;
|
|
1330
|
+
a moved file whose content also changed follows as a
|
|
1331
|
+
whole-file replacement at its destination. House keeps the document's identity
|
|
1332
|
+
and rewrites the links and references that name it; Kit reads those rewrites
|
|
1333
|
+
through `POST /kit/door/enumerate` and records them in the acknowledgment.
|
|
1334
|
+
Anything else records no move and the push sends an ordinary removal and
|
|
1335
|
+
creation, which House refuses while another document still refers to the
|
|
1336
|
+
removed one: a failed `git mv`, one undone or never committed through the
|
|
1337
|
+
shim, and a move made with Git outside the shim such as in the owner's own
|
|
1338
|
+
shell. An accepted push drops the moves its commit's history holds.
|
|
1339
|
+
|
|
1340
|
+
House decides before any local Git step. A refusal changes nothing in House;
|
|
1341
|
+
after `edit_conflict` Kit records the current content House names for the
|
|
1342
|
+
refused paths in `refs/house/received`, reading by revision any content the
|
|
1343
|
+
refusal does not carry, so the agent merges or rebases onto it with native Git,
|
|
1344
|
+
commits, and pushes again. After acceptance Kit records House's result for the
|
|
1345
|
+
pushed paths, the contents an owner push receives or each accepted revision
|
|
1346
|
+
read by name, in a commit that covers the pushed one and in
|
|
1347
|
+
`refs/house/received`; a read-only path House regenerates, such as the Wiki
|
|
1348
|
+
index, stays out of Git. The copy's recorded position goes back to where it
|
|
1349
|
+
stood when the push was sent, so the next delivery brings again anything House
|
|
1350
|
+
changed after the push's result, including what delivery received while the
|
|
1351
|
+
push awaited its answer. Kit then
|
|
1352
|
+
fast-forwards a clean branch still at the pushed commit, or rebases only its
|
|
1353
|
+
later commits. A result not yet readable, uncommitted tracked changes,
|
|
1354
|
+
a moved branch or a rebase conflict leave the branch as it was, with no stash or
|
|
1355
|
+
reset, and the push answers that House accepted it while local integration is
|
|
1356
|
+
pending; the next `house git push` finishes that integration without another
|
|
1357
|
+
House edit. A push whose answer was lost is recorded in Kit home before it is
|
|
1358
|
+
sent, and the next push by the same Session, or by the owner for an owner push,
|
|
1359
|
+
resends it under its original operation. Another caller cannot
|
|
1360
|
+
recover it: while the pushing Session lives it is refused as
|
|
1361
|
+
`kit_working_copy_push_pending`, and once that Session has ended Kit reads House
|
|
1362
|
+
again and drops it. When a retained push's operation has expired, Kit reads the
|
|
1363
|
+
Room again: if House holds the pushed content the push finishes as accepted,
|
|
1364
|
+
otherwise it is dropped and the next push submits it as a new operation.
|
|
1365
|
+
|
|
1366
|
+
Deselecting a Room, or losing read access to it, marks its copy inactive:
|
|
1367
|
+
launches omit it, delivery stops, a new push is refused as
|
|
1368
|
+
`kit_working_copy_inactive`, and nothing local is deleted; a push already sent still settles and integrates. Selecting it again reads the Room
|
|
1369
|
+
again, advances `refs/house/received`, and replaces only the read-only content
|
|
1370
|
+
Kit wrote, never the branch, the index or a tracked file. A Room the owner may
|
|
1371
|
+
read without sync is refused as `operation_denied` and stays uncopied, while its
|
|
1372
|
+
ordinary remote reads are unaffected. Kit names each refused copy on its
|
|
1373
|
+
standard error.
|
|
1374
|
+
|
|
1107
1375
|
## Explicit synchronization
|
|
1108
1376
|
|
|
1109
|
-
Beside
|
|
1377
|
+
Beside [working copies](#working-copies), Kit has one explicit synchronization mode.
|
|
1110
1378
|
It selects prefixes over the User root, materializes them into a named local
|
|
1111
1379
|
directory, enumerates whole content and removals after the recorded positions,
|
|
1112
1380
|
and submits a resolved exact-base change set the caller prepared. Kit does no
|
|
1113
1381
|
local reconciliation. The mode shares no Working Copy manifest, never infers a
|
|
1114
|
-
removal from a locally absent file, and writes authored mutations only
|
|
1115
|
-
|
|
1382
|
+
removal from a locally absent file, and writes authored mutations only within
|
|
1383
|
+
House's declared write scopes: a selected Room's Library and, where granted,
|
|
1384
|
+
`ROOM.md`. `/private` selects the Private Room by its Room reference, records an independent Room
|
|
1385
|
+
position, and keeps its `agents/` cards and Room-specific private `AGENTS.md`
|
|
1386
|
+
beside `library/**`.
|
|
1116
1387
|
|
|
1117
1388
|
```sh
|
|
1118
1389
|
kit sync bootstrap ./replica \
|
|
@@ -1137,8 +1408,7 @@ Library paths are House metadata, not authored Library documents. `pull` present
|
|
|
1137
1408
|
each recorded position with its log epoch, applies each enumerated path's whole
|
|
1138
1409
|
content or removal, and advances those positions. A position older than House's
|
|
1139
1410
|
declared retention, or one of a log epoch before House's database was restored,
|
|
1140
|
-
is reported as `position_expired
|
|
1141
|
-
`rebootstrap` rebuilds the directory.
|
|
1411
|
+
is reported as `position_expired`; only `rebootstrap` rebuilds the directory.
|
|
1142
1412
|
|
|
1143
1413
|
`submit` sends field, section, and preamble operations with that value's prior
|
|
1144
1414
|
content as base, and whole-file, rename, and removal operations with the file
|
|
@@ -1179,7 +1449,7 @@ for a batch that reaches it.
|
|
|
1179
1449
|
|
|
1180
1450
|
## Attachments
|
|
1181
1451
|
|
|
1182
|
-
Upload one local file as an attachment to a Room or to your
|
|
1452
|
+
Upload one local file as an attachment to a Room or to your Private Room. Name
|
|
1183
1453
|
the place explicitly. Kit sends the descriptor and the selected bytes; it does
|
|
1184
1454
|
not scan a directory, convert a URL, or attach sibling files, credentials, or
|
|
1185
1455
|
absolute paths.
|
|
@@ -1386,9 +1656,12 @@ $HOUSE_KIT_HOME/
|
|
|
1386
1656
|
state/repositories/<repository identity>.json
|
|
1387
1657
|
state/sync/<replica identity>.json
|
|
1388
1658
|
state/sync/<replica identity>.pending.json
|
|
1659
|
+
state/working-copies/rooms/<room ref>.json
|
|
1660
|
+
state/working-copies/private.json
|
|
1389
1661
|
state/attachments/<upload identity>.json
|
|
1390
1662
|
state/conversation-originals/<attachment identity>.json
|
|
1391
1663
|
state/sessions/<tmux session>.json
|
|
1664
|
+
state/update-drain
|
|
1392
1665
|
state/continuations/<run identity>.json
|
|
1393
1666
|
state/<instance>/credentials/google.json
|
|
1394
1667
|
state/<instance>/checkpoints/<service>.json
|
|
@@ -1583,7 +1856,7 @@ prepared model definitions survive a later content-mount refusal or unmount.
|
|
|
1583
1856
|
|
|
1584
1857
|
The atomic text mount includes a report with pending transfer rows. Kit then
|
|
1585
1858
|
uploads each downloaded file once as an attachment to the mount's Room or
|
|
1586
|
-
|
|
1859
|
+
Private Room, independently retrieves and compares the saved bytes, and only
|
|
1587
1860
|
after that proof succeeds rewrites every link to it, in every referring
|
|
1588
1861
|
document, to its attachment reference, such as `at_1x.q`. The reference is an
|
|
1589
1862
|
identity House resolves, not an HTTP download URL. A link's query and fragment
|
package/bin/connect-linux.sh
CHANGED
|
@@ -8,12 +8,47 @@ WORKSPACE="$HOME/AgentsHouse"
|
|
|
8
8
|
MANUAL=0
|
|
9
9
|
WORKSPACE_SELECTED=0
|
|
10
10
|
HOUSE_SELECTED=0
|
|
11
|
+
FORWARDING=0
|
|
12
|
+
FORWARDED=kit
|
|
13
|
+
FORWARD=()
|
|
11
14
|
|
|
12
15
|
refuse() {
|
|
13
16
|
printf 'kit_bootstrap_refused: %s\n' "$1" >&2
|
|
14
17
|
exit 1
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
bind_mount() {
|
|
21
|
+
local source=${1//\"/\"\"}
|
|
22
|
+
printf 'type=bind,"src=%s",dst=%s' "$source" "$2"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
login_kit() {
|
|
26
|
+
if [[ " $* " == *' --manual '* ]]; then
|
|
27
|
+
"${DOCKER_RUN[@]:0:2}" -i "${DOCKER_RUN[@]:2}" login "$@"
|
|
28
|
+
return
|
|
29
|
+
fi
|
|
30
|
+
command -v xdg-open >/dev/null || refuse 'no host browser opener; rerun with --manual'
|
|
31
|
+
"${DOCKER_RUN[@]}" login "$@" --host-browser | while IFS= read -r line; do
|
|
32
|
+
if [[ "$line" == 'Open '* ]]; then
|
|
33
|
+
printf 'Opening House Login in the host browser.\n'
|
|
34
|
+
xdg-open "${line#Open }" >/dev/null 2>&1 || refuse 'host browser did not open; rerun with --manual'
|
|
35
|
+
else
|
|
36
|
+
printf '%s\n' "$line"
|
|
37
|
+
fi
|
|
38
|
+
done
|
|
39
|
+
}
|
|
40
|
+
|
|
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 --house "$HOUSE" >/dev/null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if [[ "${1:-}" == kit || "${1:-}" == house ]]; then
|
|
46
|
+
FORWARDING=1
|
|
47
|
+
FORWARDED="$1"
|
|
48
|
+
FORWARD=("${@:2}")
|
|
49
|
+
set --
|
|
50
|
+
fi
|
|
51
|
+
|
|
17
52
|
while (($#)); do
|
|
18
53
|
case "$1" in
|
|
19
54
|
--workspace|--house)
|
|
@@ -53,6 +88,10 @@ case "$DOCKER_PLATFORM" in
|
|
|
53
88
|
*) refuse "unsupported Docker Engine platform $DOCKER_PLATFORM" ;;
|
|
54
89
|
esac
|
|
55
90
|
[[ "$DOCKER_ARCH" == "$ARCH" ]] || refuse "host linux/$ARCH does not match Docker Engine linux/$DOCKER_ARCH"
|
|
91
|
+
if ((FORWARDING)); then
|
|
92
|
+
docker container inspect "$NAME" >/dev/null 2>&1 || refuse 'House Kit is not installed; run this bootstrap without arguments first'
|
|
93
|
+
WORKSPACE=$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/agents/house"}}{{.Source}}{{end}}{{end}}' "$NAME")
|
|
94
|
+
fi
|
|
56
95
|
|
|
57
96
|
umask 077
|
|
58
97
|
KIT_HOME="$HOME/.house-kit"
|
|
@@ -65,6 +104,8 @@ for path in "$KIT_HOME" "$WORKSPACE"; do
|
|
|
65
104
|
done
|
|
66
105
|
KIT_HOME=$(cd "$KIT_HOME" && pwd -P)
|
|
67
106
|
WORKSPACE=$(cd "$WORKSPACE" && pwd -P)
|
|
107
|
+
KIT_HOME_MOUNT=$(bind_mount "$KIT_HOME" /kit-home)
|
|
108
|
+
WORKSPACE_MOUNT=$(bind_mount "$WORKSPACE" /agents/house)
|
|
68
109
|
if [[ -f "$KIT_HOME/credential" || -f "$KIT_HOME/environment" ]] && [[ ! -f "$KIT_HOME/house" ]]; then
|
|
69
110
|
refuse 'stored House origin is missing for an enrolled Environment'
|
|
70
111
|
fi
|
|
@@ -76,10 +117,11 @@ if [[ -f "$KIT_HOME/house" ]]; then
|
|
|
76
117
|
HOUSE="$BOUND_HOUSE"
|
|
77
118
|
fi
|
|
78
119
|
[[ "$HOUSE" == https://* || "$HOUSE" == http://127.0.0.1:* || "$HOUSE" == http://localhost:* ]] || refuse 'House origin must use HTTPS or local loopback HTTP'
|
|
79
|
-
DOCKER_RUN=(docker run --rm --network host --user "$(id -u):$(id -g)" --mount "
|
|
120
|
+
DOCKER_RUN=(docker run --rm --network host --user "$(id -u):$(id -g)" --mount "$KIT_HOME_MOUNT" --mount "$WORKSPACE_MOUNT" --env HOME=/kit-home --env HOUSE_KIT_HOME=/kit-home --env HOUSE_KIT_CONTAINER=1 --env "HOUSE_KIT_HOSTNAME=$(hostname)" "$IMAGE")
|
|
80
121
|
|
|
81
122
|
if docker container inspect "$NAME" >/dev/null 2>&1; then
|
|
82
|
-
|
|
123
|
+
INSTALLED_IMAGE=$(docker inspect --format '{{.Config.Image}}' "$NAME")
|
|
124
|
+
((FORWARDING == 0)) || [[ "$INSTALLED_IMAGE" == "$IMAGE" ]] || refuse 'an installed Kit uses a different image; rerun this bootstrap without arguments to update it'
|
|
83
125
|
[[ "$(docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' "$NAME")" == unless-stopped ]] || refuse 'the installed Kit has a different lifecycle'
|
|
84
126
|
[[ "$(docker inspect --format '{{len .Mounts}}' "$NAME")" == 2 ]] || refuse 'the installed Kit has unexpected mounts'
|
|
85
127
|
[[ "$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/kit-home"}}{{.Source}}{{end}}{{end}}' "$NAME")" == "$KIT_HOME" ]] || refuse 'the installed Kit uses another Kit home'
|
|
@@ -88,6 +130,44 @@ if docker container inspect "$NAME" >/dev/null 2>&1; then
|
|
|
88
130
|
[[ "$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/agents/house"}}{{.Type}}/{{.RW}}{{end}}{{end}}' "$NAME")" == bind/true ]] || refuse 'the installed workspace is not a writable bind mount'
|
|
89
131
|
[[ "$(docker inspect --format '{{index .Config.Labels "agentshouse.house"}}' "$NAME")" == "$HOUSE" ]] || refuse 'the installed Kit uses another House origin'
|
|
90
132
|
[[ -f "$KIT_HOME/credential" && -f "$KIT_HOME/environment" && -f "$KIT_HOME/house" ]] || refuse 'the installed Kit has no enrolled authority'
|
|
133
|
+
if ((FORWARDING)); then
|
|
134
|
+
if [[ "$FORWARDED" == kit && "${FORWARD[0]:-}" == login ]]; then
|
|
135
|
+
login_kit "${FORWARD[@]:1}"
|
|
136
|
+
exit 0
|
|
137
|
+
fi
|
|
138
|
+
PLACED=()
|
|
139
|
+
ENTRY=()
|
|
140
|
+
if [[ "$FORWARDED" == house ]]; then
|
|
141
|
+
HERE=$(pwd -P)
|
|
142
|
+
[[ "$HERE" == "$WORKSPACE" || "$HERE" == "$WORKSPACE"/* ]] || refuse "the current directory is outside the workspace $WORKSPACE"
|
|
143
|
+
PLACED=(--workdir "/agents/house${HERE#"$WORKSPACE"}")
|
|
144
|
+
ENTRY=(--entrypoint house)
|
|
145
|
+
fi
|
|
146
|
+
TERMINAL=()
|
|
147
|
+
if [[ -t 0 && -t 1 ]]; then TERMINAL=(-t); fi
|
|
148
|
+
if [[ -t 0 && ! -t 1 ]]; then
|
|
149
|
+
ECHOED=$(stty -g)
|
|
150
|
+
trap 'stty "$ECHOED"' EXIT
|
|
151
|
+
stty -echo
|
|
152
|
+
fi
|
|
153
|
+
if [[ "$(docker inspect --format '{{.State.Running}}' "$NAME")" == true ]]; then
|
|
154
|
+
docker exec -i "${TERMINAL[@]}" "${PLACED[@]}" "$NAME" "$FORWARDED" "${FORWARD[@]}"
|
|
155
|
+
else
|
|
156
|
+
"${DOCKER_RUN[@]:0:2}" -i "${TERMINAL[@]}" "${PLACED[@]}" "${ENTRY[@]}" "${DOCKER_RUN[@]:2}" "${FORWARD[@]}"
|
|
157
|
+
fi
|
|
158
|
+
exit
|
|
159
|
+
fi
|
|
160
|
+
if [[ "$INSTALLED_IMAGE" != "$IMAGE" ]]; then
|
|
161
|
+
docker pull --quiet --platform "linux/$ARCH" "$IMAGE" >/dev/null
|
|
162
|
+
if [[ "$(docker inspect --format '{{.State.Running}}' "$NAME")" == true ]]; then
|
|
163
|
+
docker exec "$NAME" kit resident drain
|
|
164
|
+
fi
|
|
165
|
+
docker rm -f "$NAME" >/dev/null
|
|
166
|
+
rm -f "$KIT_HOME/state/update-drain"
|
|
167
|
+
start_resident
|
|
168
|
+
printf 'House Kit updated for Environment %s.\n' "$(cat "$KIT_HOME/environment")"
|
|
169
|
+
exit 0
|
|
170
|
+
fi
|
|
91
171
|
"${DOCKER_RUN[@]}" credential check --house "$HOUSE"
|
|
92
172
|
if [[ "$(docker inspect --format '{{.State.Running}}' "$NAME")" == true ]]; then
|
|
93
173
|
printf 'House Kit is already running for Environment %s.\n' "$(cat "$KIT_HOME/environment")"
|
|
@@ -108,20 +188,12 @@ if [[ -f "$KIT_HOME/credential" ]]; then
|
|
|
108
188
|
"${DOCKER_RUN[@]}" credential check --house "$HOUSE"
|
|
109
189
|
else
|
|
110
190
|
if ((MANUAL)); then
|
|
111
|
-
|
|
191
|
+
login_kit --house "$HOUSE" --manual
|
|
112
192
|
else
|
|
113
|
-
|
|
114
|
-
"${DOCKER_RUN[@]}" login --house "$HOUSE" --host-browser | while IFS= read -r line; do
|
|
115
|
-
if [[ "$line" == 'Open '* ]]; then
|
|
116
|
-
printf 'Opening House Login in the host browser.\n'
|
|
117
|
-
xdg-open "${line#Open }" >/dev/null 2>&1 || refuse 'host browser did not open; rerun with --manual'
|
|
118
|
-
else
|
|
119
|
-
printf '%s\n' "$line"
|
|
120
|
-
fi
|
|
121
|
-
done
|
|
193
|
+
login_kit --house "$HOUSE"
|
|
122
194
|
fi
|
|
123
195
|
fi
|
|
124
196
|
|
|
125
197
|
"${DOCKER_RUN[@]}" apply --house "$HOUSE"
|
|
126
|
-
|
|
198
|
+
start_resident
|
|
127
199
|
printf 'House Kit connected for Environment %s.\n' "$(cat "$KIT_HOME/environment")"
|
package/bin/kit.js
CHANGED
|
@@ -9,7 +9,7 @@ import { setupEnvironment } from "../src/setup.js";
|
|
|
9
9
|
import { signInConnector } from "../src/connector-login.js";
|
|
10
10
|
import { attachSession } from "../src/attach.js";
|
|
11
11
|
import { runResident, runWorkers } from "../src/worker.js";
|
|
12
|
-
import { openSession, releaseControl } from "../src/house.js";
|
|
12
|
+
import { openSession, releaseControl, requestDrain } from "../src/house.js";
|
|
13
13
|
import { GOOGLE_OAUTH_CLIENT } from "../src/instance/google.js";
|
|
14
14
|
import { reportHost } from "../src/host.js";
|
|
15
15
|
import { signInAgent, updateAgents } from "../src/agents.js";
|
|
@@ -140,7 +140,7 @@ async function runCredentialCheck(args, directory) {
|
|
|
140
140
|
const house = await selectedHouse(values, directory);
|
|
141
141
|
let session;
|
|
142
142
|
try {
|
|
143
|
-
session = await openSession({ house, directory });
|
|
143
|
+
session = await openSession({ house, directory, acquire: true });
|
|
144
144
|
} catch (error) {
|
|
145
145
|
if (error instanceof KitRefusal && error.code === "environment_control_lease_active") {
|
|
146
146
|
write("Stored Kit authority is accepted; its Environment already has an active control lease.");
|
|
@@ -167,6 +167,23 @@ async function runResidentCommand(args, directory) {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
async function runResidentDrain(args, directory) {
|
|
171
|
+
declared(args, [], 0, { exact: true });
|
|
172
|
+
const answer = await requestDrain(directory);
|
|
173
|
+
if (answer.ready === true) {
|
|
174
|
+
write("House Kit is ready for its update.");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
write(
|
|
178
|
+
answer.sessions.length > 0
|
|
179
|
+
? `House Kit is draining for its update and waits for Agent Sessions ${answer.sessions.join(", ")}.`
|
|
180
|
+
: answer.serving === true
|
|
181
|
+
? "House Kit is draining for its update and waits for the Kit command it serves."
|
|
182
|
+
: `House Kit is draining for its update; House refused its lease release (${answer.refused}).`,
|
|
183
|
+
);
|
|
184
|
+
process.exitCode = 1;
|
|
185
|
+
}
|
|
186
|
+
|
|
170
187
|
async function runSetup(args, directory) {
|
|
171
188
|
const { values } = declared(args, ["--house", "--config"], 0, { exact: true });
|
|
172
189
|
const house = await selectedHouse(values, directory);
|
|
@@ -965,7 +982,10 @@ async function main() {
|
|
|
965
982
|
const directory = process.env.HOUSE_KIT_HOME ?? join(homedir(), ".house-kit");
|
|
966
983
|
if (command === "login") return runLogin(args, directory);
|
|
967
984
|
if (command === "credential" && args[0] === "check") return runCredentialCheck(args.slice(1), directory);
|
|
968
|
-
if (command === "resident")
|
|
985
|
+
if (command === "resident") {
|
|
986
|
+
if (args[0] === "drain") return runResidentDrain(args.slice(1), directory);
|
|
987
|
+
return runResidentCommand(args, directory);
|
|
988
|
+
}
|
|
969
989
|
if (command === "setup") return runSetup(args, directory);
|
|
970
990
|
if (command === "apply") return runApply(args, directory);
|
|
971
991
|
if (command === "worker") {
|