@agentshouse/kit 0.1.0-alpha.4 → 0.1.0-alpha.41

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.
Files changed (150) hide show
  1. package/README.md +41 -1671
  2. package/bin/connect-linux.sh +501 -53
  3. package/bin/connect-windows.ps1 +440 -0
  4. package/bin/kit.js +200 -159
  5. package/bootstrap.json +28 -0
  6. package/docs/configuration.md +104 -0
  7. package/docs/google.md +33 -0
  8. package/docs/linux.md +77 -0
  9. package/docs/macos.md +26 -0
  10. package/docs/telegram.md +29 -0
  11. package/docs/windows.md +37 -0
  12. package/package.json +7 -2
  13. package/src/adapter-mcp.js +92 -12
  14. package/src/agents.js +63 -8
  15. package/src/binding.js +52 -0
  16. package/src/blocks.js +2 -26
  17. package/src/bridge.js +28 -14
  18. package/src/bytes.js +19 -8
  19. package/src/configuration.js +14 -211
  20. package/src/connector-login.js +3 -84
  21. package/src/contract.js +44 -12
  22. package/src/conversation-originals.js +1 -3
  23. package/src/delivery.js +4 -7
  24. package/src/directory.js +6 -26
  25. package/src/draft.js +4 -82
  26. package/src/git-main.js +138 -0
  27. package/src/git.js +62 -0
  28. package/src/google/calendar.js +39 -45
  29. package/src/google/drive.js +61 -186
  30. package/src/google/entries.js +349 -0
  31. package/src/google/gateway.js +3 -6
  32. package/src/google/gmail.js +33 -23
  33. package/src/google/oauth.js +106 -66
  34. package/src/google/source.js +21 -23
  35. package/src/host.js +6 -2
  36. package/src/house-main.js +65 -8
  37. package/src/house.js +446 -127
  38. package/src/how-we-work.md +25 -0
  39. package/src/inbound.js +2 -18
  40. package/src/kit-work-stream.js +120 -0
  41. package/src/launcher.js +46 -10
  42. package/src/local-files.js +3 -11
  43. package/src/login.js +27 -14
  44. package/src/mcp.js +18 -0
  45. package/src/mount.js +9 -18
  46. package/src/original.js +4 -4
  47. package/src/pacing.js +41 -0
  48. package/src/plan.js +37 -45
  49. package/src/repository.js +3 -61
  50. package/src/session-bin/git +2 -0
  51. package/src/setup.js +14 -15
  52. package/src/skill-set/ask-matt/PHASE-BOUNDARIES.md +55 -0
  53. package/src/skill-set/ask-matt/SKILL.md +91 -0
  54. package/src/skill-set/ask-matt/agents/openai.yaml +4 -0
  55. package/src/skill-set/code-review/LICENSE +21 -0
  56. package/src/skill-set/code-review/SKILL.md +89 -0
  57. package/src/skill-set/code-review/agents/openai.yaml +3 -0
  58. package/src/skill-set/codebase-design/DEEPENING.md +37 -0
  59. package/src/skill-set/codebase-design/DESIGN-IT-TWICE.md +44 -0
  60. package/src/skill-set/codebase-design/LICENSE +21 -0
  61. package/src/skill-set/codebase-design/SKILL.md +116 -0
  62. package/src/skill-set/codebase-design/agents/openai.yaml +3 -0
  63. package/src/skill-set/diagnosing-bugs/LICENSE +21 -0
  64. package/src/skill-set/diagnosing-bugs/SKILL.md +140 -0
  65. package/src/skill-set/diagnosing-bugs/agents/openai.yaml +3 -0
  66. package/src/skill-set/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
  67. package/src/skill-set/domain-modeling/ADR-FORMAT.md +47 -0
  68. package/src/skill-set/domain-modeling/CONTEXT-FORMAT.md +60 -0
  69. package/src/skill-set/domain-modeling/LICENSE +21 -0
  70. package/src/skill-set/domain-modeling/SKILL.md +76 -0
  71. package/src/skill-set/domain-modeling/agents/openai.yaml +3 -0
  72. package/src/skill-set/grill-me/LICENSE +21 -0
  73. package/src/skill-set/grill-me/SKILL.md +8 -0
  74. package/src/skill-set/grill-me/agents/openai.yaml +4 -0
  75. package/src/skill-set/grill-with-docs/LICENSE +21 -0
  76. package/src/skill-set/grill-with-docs/SKILL.md +8 -0
  77. package/src/skill-set/grill-with-docs/agents/openai.yaml +4 -0
  78. package/src/skill-set/grilling/LICENSE +21 -0
  79. package/src/{starter-set → skill-set}/grilling/SKILL.md +1 -1
  80. package/src/skill-set/handoff/LICENSE +21 -0
  81. package/src/skill-set/handoff/SKILL.md +17 -0
  82. package/src/skill-set/handoff/agents/openai.yaml +4 -0
  83. package/src/skill-set/implement/LICENSE +21 -0
  84. package/src/skill-set/implement/SKILL.md +16 -0
  85. package/src/skill-set/implement/agents/openai.yaml +4 -0
  86. package/src/skill-set/improve-codebase-architecture/HTML-REPORT.md +123 -0
  87. package/src/skill-set/improve-codebase-architecture/LICENSE +21 -0
  88. package/src/skill-set/improve-codebase-architecture/SKILL.md +72 -0
  89. package/src/skill-set/improve-codebase-architecture/agents/openai.yaml +4 -0
  90. package/src/skill-set/prototype/LICENSE +21 -0
  91. package/src/skill-set/prototype/LOGIC.md +67 -0
  92. package/src/skill-set/prototype/SKILL.md +28 -0
  93. package/src/skill-set/prototype/UI.md +112 -0
  94. package/src/skill-set/prototype/agents/openai.yaml +3 -0
  95. package/src/skill-set/research/LICENSE +21 -0
  96. package/src/skill-set/research/SKILL.md +14 -0
  97. package/src/skill-set/research/agents/openai.yaml +3 -0
  98. package/src/skill-set/resolving-merge-conflicts/LICENSE +21 -0
  99. package/src/skill-set/resolving-merge-conflicts/SKILL.md +16 -0
  100. package/src/skill-set/resolving-merge-conflicts/agents/openai.yaml +3 -0
  101. package/src/skill-set/tdd/LICENSE +21 -0
  102. package/src/skill-set/tdd/SKILL.md +40 -0
  103. package/src/skill-set/tdd/agents/openai.yaml +3 -0
  104. package/src/skill-set/tdd/mocking.md +59 -0
  105. package/src/skill-set/tdd/tests.md +77 -0
  106. package/src/skill-set/teach/GLOSSARY-FORMAT.md +35 -0
  107. package/src/skill-set/teach/LEARNING-RECORD-FORMAT.md +46 -0
  108. package/src/skill-set/teach/LICENSE +21 -0
  109. package/src/skill-set/teach/MISSION-FORMAT.md +31 -0
  110. package/src/skill-set/teach/RESOURCES-FORMAT.md +32 -0
  111. package/src/skill-set/teach/SKILL.md +141 -0
  112. package/src/skill-set/teach/agents/openai.yaml +4 -0
  113. package/src/skill-set/to-questionnaire/LICENSE +21 -0
  114. package/src/skill-set/to-questionnaire/SKILL.md +55 -0
  115. package/src/skill-set/to-questionnaire/agents/openai.yaml +4 -0
  116. package/src/skill-set/to-spec/LICENSE +21 -0
  117. package/src/skill-set/to-spec/SKILL.md +76 -0
  118. package/src/skill-set/to-spec/agents/openai.yaml +4 -0
  119. package/src/skill-set/to-tickets/LICENSE +21 -0
  120. package/src/skill-set/to-tickets/SKILL.md +106 -0
  121. package/src/skill-set/to-tickets/agents/openai.yaml +4 -0
  122. package/src/skill-set/wait-what/LICENSE +21 -0
  123. package/src/skill-set/wait-what/SKILL.md +8 -0
  124. package/src/skill-set/wait-what/agents/openai.yaml +4 -0
  125. package/src/skill-set/wayfinder/LICENSE +21 -0
  126. package/src/skill-set/wayfinder/SKILL.md +129 -0
  127. package/src/skill-set/wayfinder/agents/openai.yaml +4 -0
  128. package/src/skill-set/writing-for-agents/LICENSE +21 -0
  129. package/src/skill-set/writing-for-agents/SKILL-MECHANICS.md +22 -0
  130. package/src/skill-set/writing-for-agents/SKILL.md +83 -0
  131. package/src/skill-set/writing-for-agents/agents/openai.yaml +3 -0
  132. package/src/skill-set.js +85 -0
  133. package/src/source.js +93 -54
  134. package/src/state.js +19 -1
  135. package/src/sync-library.js +7 -11
  136. package/src/sync-state.js +12 -3
  137. package/src/sync.js +142 -43
  138. package/src/tasks.js +92 -0
  139. package/src/telegram-view.js +217 -0
  140. package/src/telegram.js +204 -69
  141. package/src/turn-frames.js +69 -0
  142. package/src/worker.js +738 -189
  143. package/src/working-copy.js +1274 -0
  144. package/src/workspace.js +299 -88
  145. package/src/google/declared.js +0 -146
  146. package/src/google/id.js +0 -1
  147. package/src/instance/google.js +0 -1
  148. package/src/starter-set.js +0 -17
  149. /package/src/{starter-set/grilling → skill-set/ask-matt}/LICENSE +0 -0
  150. /package/src/{starter-set → skill-set}/grilling/agents/openai.yaml +0 -0
package/bin/kit.js CHANGED
@@ -2,18 +2,18 @@
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
+ import { accountEntries, accountStatus, captureAccount } from "../src/google/entries.js";
8
9
  import { setupEnvironment } from "../src/setup.js";
9
10
  import { signInConnector } from "../src/connector-login.js";
10
11
  import { attachSession } from "../src/attach.js";
11
12
  import { runResident, runWorkers } from "../src/worker.js";
12
- import { openSession, releaseControl } from "../src/house.js";
13
- import { GOOGLE_OAUTH_CLIENT } from "../src/instance/google.js";
13
+ import { openSession, releaseControl, requestDrain } from "../src/house.js";
14
14
  import { reportHost } from "../src/host.js";
15
15
  import { signInAgent, updateAgents } from "../src/agents.js";
16
- import { serveWorkspaces } from "../src/workspace.js";
16
+ import { serveWorkspaces, updateWorkspaces } from "../src/workspace.js";
17
17
  import { captureRepository } from "../src/repository.js";
18
18
  import { bootstrap, pull, rebootstrap, submit } from "../src/sync.js";
19
19
  import { formatMount, formatUnmount, mountPackage, unmountSubtree } from "../src/mount.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 [--house <url>] [--config <path>]";
35
+ const HOST_USAGE = "usage: kit host report [--config <path>]";
36
36
  const AGENTS_USAGE =
37
- "usage: kit agents update [--house <url>] [--config <path>] | kit agents sign-in <kind> [--house <url>] [--config <path>]";
38
- const WORKSPACE_USAGE = "usage: kit workspace host [--house <url>] [--config <path>] [--passes <count>]";
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>] | kit workspace update";
39
39
  const SOURCE_USAGE =
40
- "usage: kit source <capture|reset|retry-files> <instance> [service] [--house <url>] [--config <path>]\n" +
41
- " kit source status <instance> [service] [--config <path>]\n" +
40
+ "usage: kit source <capture|status> <account|instance> [service] [--config <path>]\n" +
41
+ " kit source <reset|retry-files> <instance> [service] [--config <path>]\n" +
42
42
  " kit source sign-in <instance> [--config <path>]";
43
- const SETUP_USAGE = "usage: kit setup [--house <url>] [--config <path>]";
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> [--house <url>]";
45
+ "usage: kit repository capture <checkout> --room <room> --source-handle <handle>";
46
46
  const WORKER_USAGE =
47
- "usage: kit worker run [--house <url>] [--config <path>] | kit worker attach [<session>]";
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> [--house <url>] --prefix <path> [--prefix <path> ...]";
52
- const SYNC_PULL_USAGE = "usage: kit sync pull <directory> [--house <url>]";
53
- const SYNC_REBOOTSTRAP_USAGE = "usage: kit sync rebootstrap <directory> [--house <url>]";
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> [--house <url>] [--changes <path>]";
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> [--house <url>] --subtree <path>";
60
+ "usage: kit migrate mount <package> --subtree <path>";
61
61
  const MIGRATE_UNMOUNT_USAGE =
62
- "usage: kit migrate unmount <subtree> [--house <url>] [--confirm <subtree>]";
62
+ "usage: kit migrate unmount <subtree> [--confirm <subtree>]";
63
63
  const MIGRATE_RUN_USAGE =
64
- "usage: kit migrate run <migrator> [--house <url>] --subtree <path> --package <directory> [--config <path>]";
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> [--house <url>] (--room <ref> | --private) [--name <name>] [--media-type <type>]";
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 [--house <url>] --attachment <at_ref> (--room <ref> | --private) [--output <path>]";
69
+ "usage: kit attachment get --attachment <at_ref> (--room <ref> | --private) [--output <path>]";
70
70
  const TELEGRAM_USAGE =
71
- "usage: kit telegram <connect|disconnect> [--house <url>]\n" +
72
- " kit telegram serve [--house <url>] [--passes <count>]";
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 ?? [];
@@ -108,21 +108,10 @@ function write(line) {
108
108
  process.stdout.write(`${line}\n`);
109
109
  }
110
110
 
111
- function builtGoogleOAuthClient() {
112
- if (GOOGLE_OAUTH_CLIENT === null) {
113
- throw new KitRefusal("google_oauth_client_unconfigured", "run npm run build");
114
- }
115
- return GOOGLE_OAUTH_CLIENT;
116
- }
117
-
118
- async function selectedHouse(values, directory) {
119
- return storedHouse(directory, false, values["--house"]);
120
- }
121
-
122
111
  async function runLogin(args, directory) {
123
112
  const { values } = declared(args, ["--house", "--manual", "--host-browser", "--environment"], 0, { flags: ["--host-browser"] });
124
113
  if (values["--manual"] && values["--host-browser"]) throw new Error(LOGIN_USAGE);
125
- const house = await storedHouse(directory, true, values["--house"]);
114
+ const house = await loginHouse(directory, values["--house"]);
126
115
  const presentation = values["--manual"] === true ? "manual" : "loopback";
127
116
  await connectHouse({
128
117
  house,
@@ -130,17 +119,18 @@ async function runLogin(args, directory) {
130
119
  directory,
131
120
  hostBrowser: values["--host-browser"] === true,
132
121
  hostname: process.env.HOUSE_KIT_HOSTNAME,
122
+ loginPort: process.env.HOUSE_KIT_LOGIN_PORT,
133
123
  ...(values["--environment"] === undefined ? {} : { environment: values["--environment"] }),
134
124
  });
135
125
  }
136
126
 
137
127
  async function runCredentialCheck(args, directory) {
138
- const { values } = declared(args, ["--house"], 0, { exact: true });
128
+ declared(args, [], 0, { exact: true });
139
129
  if (await storedEnvironment(directory) === null) throw new KitRefusal("kit_environment_invalid", "no enrolled Environment id");
140
- const house = await selectedHouse(values, directory);
130
+ const house = await storedHouse(directory);
141
131
  let session;
142
132
  try {
143
- session = await openSession({ house, directory });
133
+ session = await openSession({ house, directory, acquire: true });
144
134
  } catch (error) {
145
135
  if (error instanceof KitRefusal && error.code === "environment_control_lease_active") {
146
136
  write("Stored Kit authority is accepted; its Environment already has an active control lease.");
@@ -153,8 +143,8 @@ async function runCredentialCheck(args, directory) {
153
143
  }
154
144
 
155
145
  async function runResidentCommand(args, directory) {
156
- const { values } = declared(args, ["--house", "--config"], 0, { exact: true });
157
- const house = await selectedHouse(values, directory);
146
+ const { values } = declared(args, ["--config"], 0, { exact: true });
147
+ const house = await storedHouse(directory);
158
148
  const cancellation = new AbortController();
159
149
  const stop = () => cancellation.abort();
160
150
  process.once("SIGINT", stop);
@@ -167,24 +157,37 @@ async function runResidentCommand(args, directory) {
167
157
  }
168
158
  }
169
159
 
160
+ async function runResidentDrain(args, directory) {
161
+ declared(args, [], 0, { exact: true });
162
+ const answer = await requestDrain(directory);
163
+ if (answer.ready === true) {
164
+ write("House Kit is ready for its update.");
165
+ return;
166
+ }
167
+ write(
168
+ answer.sessions.length > 0
169
+ ? `House Kit is draining for its update and waits for Agent Sessions ${answer.sessions.join(", ")}.`
170
+ : answer.serving === true
171
+ ? "House Kit is draining for its update and waits for the Kit command it serves."
172
+ : `House Kit is draining for its update; House refused its lease release (${answer.refused}).`,
173
+ );
174
+ process.exitCode = 1;
175
+ }
176
+
170
177
  async function runSetup(args, directory) {
171
- const { values } = declared(args, ["--house", "--config"], 0, { exact: true });
172
- const house = await selectedHouse(values, directory);
173
- if (!house) throw new Error(SETUP_USAGE);
178
+ const { values } = declared(args, ["--config"], 0, { exact: true });
179
+ const house = await storedHouse(directory);
174
180
  const cancellation = new AbortController();
175
181
  const cancel = () => cancellation.abort();
176
182
  process.once("SIGINT", cancel);
177
183
  let setup;
178
184
  try {
179
- setup = await setupEnvironment(
180
- {
181
- house,
182
- directory,
183
- configuration: values["--config"] ?? join(directory, "kit.json"),
184
- signal: cancellation.signal,
185
- },
186
- { googleOAuthClient: builtGoogleOAuthClient },
187
- );
185
+ setup = await setupEnvironment({
186
+ house,
187
+ directory,
188
+ configuration: values["--config"] ?? join(directory, "kit.json"),
189
+ signal: cancellation.signal,
190
+ });
188
191
  } finally {
189
192
  process.removeListener("SIGINT", cancel);
190
193
  }
@@ -223,9 +226,8 @@ async function runSourceSignIn(args, directory) {
223
226
  }
224
227
 
225
228
  async function runApply(args, directory) {
226
- const { values } = declared(args, ["--house", "--config"], 0);
227
- const house = await selectedHouse(values, directory);
228
- if (!house) throw new Error("usage: kit apply --house <url> [--config <path>]");
229
+ const { values } = declared(args, ["--config"], 0);
230
+ const house = await storedHouse(directory);
229
231
  const cancellation = new AbortController();
230
232
  const cancel = () => cancellation.abort();
231
233
  process.once("SIGINT", cancel);
@@ -252,27 +254,32 @@ async function runApply(args, directory) {
252
254
  }
253
255
 
254
256
  async function runSourceCapture(args, directory) {
255
- const { values, rest } = declared(args, ["--house", "--config"], 2);
256
- const house = await selectedHouse(values, directory);
257
- if (!house || rest.length === 0) throw new Error(SOURCE_USAGE);
257
+ const { values, rest } = declared(args, ["--config"], 2);
258
+ if (rest.length === 0) throw new Error(SOURCE_USAGE);
259
+ const house = await storedHouse(directory);
258
260
  const cancellation = new AbortController();
259
261
  process.once("SIGINT", () => cancellation.abort());
260
- const result = await captureSource(
261
- {
262
- house,
263
- directory,
264
- configuration: values["--config"] ?? join(directory, "kit.json"),
265
- instance: rest[0],
266
- service: rest[1] ?? null,
267
- signal: cancellation.signal,
268
- },
269
- { googleOAuthClient: builtGoogleOAuthClient },
270
- );
262
+ const entries = await accountEntries(directory, rest[0]);
263
+ const options = {
264
+ house,
265
+ directory,
266
+ service: rest[1] ?? null,
267
+ signal: cancellation.signal,
268
+ };
269
+ const result =
270
+ entries.length > 0
271
+ ? await captureAccount({ ...options, account: rest[0], entries })
272
+ : await captureSource({
273
+ ...options,
274
+ configuration: values["--config"] ?? join(directory, "kit.json"),
275
+ instance: rest[0],
276
+ });
271
277
  for (const delivered of result.services) {
272
278
  const category =
273
279
  delivered.health.category === null ? "" : ` (${delivered.health.category})`;
280
+ const cause = delivered.health.cause === undefined ? "" : `, cause ${delivered.health.cause}`;
274
281
  write(
275
- `Delivered ${delivered.delivered} items for ${result.instance}/${delivered.service} in ${delivered.batches} batches; health ${delivered.health.state}${category}.`,
282
+ `Delivered ${delivered.delivered} items for ${result.instance}/${delivered.service} in ${delivered.batches} batches; health ${delivered.health.state}${category}${cause}.`,
276
283
  );
277
284
  if (delivered.refused !== null) {
278
285
  write(
@@ -297,9 +304,9 @@ function writeSourceOriginals(instance, service) {
297
304
  }
298
305
 
299
306
  async function runSourceRetryFiles(args, directory) {
300
- const { values, rest } = declared(args, ["--house", "--config"], 2);
301
- const house = await selectedHouse(values, directory);
302
- if (!house || rest.length === 0) throw new Error(SOURCE_USAGE);
307
+ const { values, rest } = declared(args, ["--config"], 2);
308
+ if (rest.length === 0) throw new Error(SOURCE_USAGE);
309
+ const house = await storedHouse(directory);
303
310
  const cancellation = new AbortController();
304
311
  process.once("SIGINT", () => cancellation.abort());
305
312
  const result = await retrySourceOriginals({
@@ -321,30 +328,40 @@ async function runSourceRetryFiles(args, directory) {
321
328
  async function runSourceStatus(args, directory) {
322
329
  const { values, rest } = declared(args, ["--config"], 2);
323
330
  if (rest.length === 0) throw new Error(SOURCE_USAGE);
324
- const result = await sourceStatus({
325
- directory,
326
- configuration: values["--config"] ?? join(directory, "kit.json"),
327
- instance: rest[0],
328
- service: rest[1] ?? null,
329
- });
331
+ const entries = await accountEntries(directory, rest[0]);
332
+ const result =
333
+ entries.length > 0
334
+ ? await accountStatus({ directory, account: rest[0], entries, service: rest[1] ?? null })
335
+ : await sourceStatus({
336
+ directory,
337
+ configuration: values["--config"] ?? join(directory, "kit.json"),
338
+ instance: rest[0],
339
+ service: rest[1] ?? null,
340
+ });
330
341
  for (const reported of result.services) {
331
342
  const category = reported.health.category === null ? "" : ` (${reported.health.category})`;
343
+ const cause = reported.health.cause === undefined ? "" : `, cause ${reported.health.cause}`;
332
344
  const observed =
333
345
  reported.health.observed_at === null ? "never observed" : reported.health.observed_at;
346
+ const signedIn =
347
+ reported.signed_in === undefined ? "" : reported.signed_in ? "signed in; " : "signed out; ";
334
348
  write(
335
349
  `${result.instance}/${reported.service} -> ${reported.source_ref} in ${reported.rooms.join(", ")}: ` +
336
- `${reported.health.state}${category} at ${observed}; ` +
350
+ `${signedIn}${reported.health.state}${category}${cause} at ${observed}; ` +
337
351
  `cursor ${reported.checkpoint?.cursor ?? "none"}, ${reported.gaps.length} gaps, ` +
338
352
  `${reported.dropped.length} dropped items, ${reported.originals.length} unsaved originals.`,
339
353
  );
354
+ if (reported.enable !== null && reported.enable !== undefined) {
355
+ write(`Enable the ${reported.enable} in the Google Cloud project of ${result.instance}'s OAuth client.`);
356
+ }
340
357
  }
341
358
  if (result.services.some((reported) => reported.health.state !== "ok")) process.exitCode = 1;
342
359
  }
343
360
 
344
361
  async function runSourceReset(args, directory) {
345
- const { values, rest } = declared(args, ["--house", "--config"], 2);
346
- const house = await selectedHouse(values, directory);
347
- if (!house || rest.length === 0) throw new Error(SOURCE_USAGE);
362
+ const { values, rest } = declared(args, ["--config"], 2);
363
+ if (rest.length === 0) throw new Error(SOURCE_USAGE);
364
+ const house = await storedHouse(directory);
348
365
  const result = await resetSource({
349
366
  house,
350
367
  directory,
@@ -360,11 +377,11 @@ async function runSourceReset(args, directory) {
360
377
  }
361
378
 
362
379
  async function runRepositoryCapture(args, directory) {
363
- const { values, rest } = declared(args, ["--house", "--room", "--source-handle"], 1);
364
- const house = await selectedHouse(values, directory);
380
+ const { values, rest } = declared(args, ["--room", "--source-handle"], 1);
365
381
  const room = values["--room"];
366
382
  const sourceHandle = values["--source-handle"];
367
- if (!house || !room || !sourceHandle || rest.length !== 1) throw new Error(REPOSITORY_USAGE);
383
+ if (!room || !sourceHandle || rest.length !== 1) throw new Error(REPOSITORY_USAGE);
384
+ const house = await storedHouse(directory);
368
385
  const cancellation = new AbortController();
369
386
  const cancel = () => cancellation.abort();
370
387
  process.once("SIGINT", cancel);
@@ -412,17 +429,13 @@ function writeConversationFiles(supervised) {
412
429
  }
413
430
 
414
431
  async function runWorkerRun(args, directory) {
415
- const { values } = declared(args, ["--house", "--config"], 0);
416
- const house = await selectedHouse(values, directory);
417
- if (!house) throw new Error(WORKER_USAGE);
418
- const supervised = await runWorkers(
419
- {
420
- house,
421
- directory,
422
- configuration: values["--config"] ?? join(directory, "kit.json"),
423
- },
424
- { googleOAuthClient: builtGoogleOAuthClient },
425
- );
432
+ const { values } = declared(args, ["--config"], 0);
433
+ const house = await storedHouse(directory);
434
+ const supervised = await runWorkers({
435
+ house,
436
+ directory,
437
+ configuration: values["--config"] ?? join(directory, "kit.json"),
438
+ });
426
439
  write(`Worker runtime ready on tmux ${supervised.tmux} in namespace ${supervised.namespace}.`);
427
440
  if (supervised.epochReport !== null) {
428
441
  write(`House started a new log epoch; reported ${supervised.epochReport.length} held Runs and House settled each.`);
@@ -466,8 +479,10 @@ async function runWorkerRun(args, directory) {
466
479
  `Run ${run.run} stays bound to this installation until House accepts that report` +
467
480
  `${run.refused === null ? "" : ` (${run.refused.code})`}.`,
468
481
  );
482
+ } else if (run.refused !== null) {
483
+ write(`House refused that report (${run.refused.code}); this installation no longer holds Run ${run.run}.`);
469
484
  }
470
- if (!run.settled || reported?.outcome === "failed") process.exitCode = 1;
485
+ if (!run.settled || run.refused !== null || reported?.outcome === "failed") process.exitCode = 1;
471
486
  }
472
487
 
473
488
  function syncCancellation() {
@@ -483,12 +498,12 @@ function syncCancellation() {
483
498
  }
484
499
 
485
500
  async function runSyncBootstrap(args, directory) {
486
- const { values, lists, rest } = declared(args, ["--house", "--prefix"], 1, {
501
+ const { lists, rest } = declared(args, ["--prefix"], 1, {
487
502
  repeated: ["--prefix"],
488
503
  exact: true,
489
504
  });
490
- const house = await selectedHouse(values, directory);
491
- if (!house || lists["--prefix"].length === 0) throw new Error(SYNC_BOOTSTRAP_USAGE);
505
+ if (lists["--prefix"].length === 0) throw new Error(SYNC_BOOTSTRAP_USAGE);
506
+ const house = await storedHouse(directory);
492
507
  const run = syncCancellation();
493
508
  try {
494
509
  const result = await bootstrap({
@@ -507,9 +522,8 @@ async function runSyncBootstrap(args, directory) {
507
522
  }
508
523
 
509
524
  async function runSyncPull(args, directory) {
510
- const { values, rest } = declared(args, ["--house"], 1, { exact: true });
511
- const house = await selectedHouse(values, directory);
512
- if (!house) throw new Error(SYNC_PULL_USAGE);
525
+ const { rest } = declared(args, [], 1, { exact: true });
526
+ const house = await storedHouse(directory);
513
527
  const run = syncCancellation();
514
528
  try {
515
529
  const result = await pull({
@@ -527,9 +541,8 @@ async function runSyncPull(args, directory) {
527
541
  }
528
542
 
529
543
  async function runSyncRebootstrap(args, directory) {
530
- const { values, rest } = declared(args, ["--house"], 1, { exact: true });
531
- const house = await selectedHouse(values, directory);
532
- if (!house) throw new Error(SYNC_REBOOTSTRAP_USAGE);
544
+ const { rest } = declared(args, [], 1, { exact: true });
545
+ const house = await storedHouse(directory);
533
546
  const run = syncCancellation();
534
547
  try {
535
548
  const result = await rebootstrap({
@@ -554,9 +567,8 @@ async function readChanges(path) {
554
567
  }
555
568
 
556
569
  async function runSyncSubmit(args, directory) {
557
- const { values, rest } = declared(args, ["--house", "--changes"], 1, { exact: true });
558
- const house = await selectedHouse(values, directory);
559
- if (!house) throw new Error(SYNC_SUBMIT_USAGE);
570
+ const { values, rest } = declared(args, ["--changes"], 1, { exact: true });
571
+ const house = await storedHouse(directory);
560
572
  const run = syncCancellation();
561
573
  try {
562
574
  const result = await submit({
@@ -629,11 +641,11 @@ async function mountEmitted(packagePath, options, run) {
629
641
  }
630
642
 
631
643
  async function runMigrateMount(args, directory) {
632
- const { values, rest } = declared(args, ["--house", "--subtree"], 1, { exact: true });
633
- const house = await selectedHouse(values, directory);
634
- if (!house || rest.length !== 1 || values["--subtree"] === undefined) {
644
+ const { values, rest } = declared(args, ["--subtree"], 1, { exact: true });
645
+ if (rest.length !== 1 || values["--subtree"] === undefined) {
635
646
  throw new Error(MIGRATE_MOUNT_USAGE);
636
647
  }
648
+ const house = await storedHouse(directory);
637
649
  const run = migrateCancellation();
638
650
  try {
639
651
  await mountEmitted(rest[0], { house, directory, subtree: values["--subtree"] }, run);
@@ -643,18 +655,17 @@ async function runMigrateMount(args, directory) {
643
655
  }
644
656
 
645
657
  async function runMigrateRun(args, directory) {
646
- const { values, rest } = declared(args, ["--house", "--subtree", "--package", "--config"], 1, {
658
+ const { values, rest } = declared(args, ["--subtree", "--package", "--config"], 1, {
647
659
  exact: true,
648
660
  });
649
- const house = await selectedHouse(values, directory);
650
661
  if (
651
- !house ||
652
662
  rest.length !== 1 ||
653
663
  values["--subtree"] === undefined ||
654
664
  values["--package"] === undefined
655
665
  ) {
656
666
  throw new Error(MIGRATE_RUN_USAGE);
657
667
  }
668
+ const house = await storedHouse(directory);
658
669
  const run = migrateCancellation();
659
670
  try {
660
671
  const emitted = await emitPackage(
@@ -671,9 +682,9 @@ async function runMigrateRun(args, directory) {
671
682
  }
672
683
 
673
684
  async function runMigrateUnmount(args, directory) {
674
- const { values, rest } = declared(args, ["--house", "--confirm"], 1, { exact: true });
675
- const house = await selectedHouse(values, directory);
676
- if (!house || rest.length !== 1) throw new Error(MIGRATE_UNMOUNT_USAGE);
685
+ const { values, rest } = declared(args, ["--confirm"], 1, { exact: true });
686
+ if (rest.length !== 1) throw new Error(MIGRATE_UNMOUNT_USAGE);
687
+ const house = await storedHouse(directory);
677
688
  const run = migrateCancellation();
678
689
  try {
679
690
  const result = await unmountSubtree({
@@ -729,12 +740,12 @@ function attachmentCancellation() {
729
740
  async function runAttachmentUpload(args, directory) {
730
741
  const { values, rest } = declared(
731
742
  args,
732
- ["--house", "--room", "--private", "--name", "--media-type"],
743
+ ["--room", "--private", "--name", "--media-type"],
733
744
  1,
734
745
  { flags: ["--private"] },
735
746
  );
736
- const house = await selectedHouse(values, directory);
737
- if (!house || rest.length !== 1) throw new Error(ATTACHMENT_UPLOAD_USAGE);
747
+ if (rest.length !== 1) throw new Error(ATTACHMENT_UPLOAD_USAGE);
748
+ const house = await storedHouse(directory);
738
749
  const run = attachmentCancellation();
739
750
  try {
740
751
  const result = await uploadAttachment({
@@ -756,13 +767,13 @@ async function runAttachmentUpload(args, directory) {
756
767
  async function runAttachmentGet(args, directory) {
757
768
  const { values } = declared(
758
769
  args,
759
- ["--house", "--attachment", "--room", "--private", "--output"],
770
+ ["--attachment", "--room", "--private", "--output"],
760
771
  0,
761
772
  { flags: ["--private"] },
762
773
  );
763
- const house = await selectedHouse(values, directory);
764
774
  const attachment = values["--attachment"];
765
- if (!house || !attachment) throw new Error(ATTACHMENT_GET_USAGE);
775
+ if (!attachment) throw new Error(ATTACHMENT_GET_USAGE);
776
+ const house = await storedHouse(directory);
766
777
  const run = attachmentCancellation();
767
778
  try {
768
779
  const result = await getAttachment({
@@ -788,9 +799,8 @@ async function runAttachment(args, directory) {
788
799
  }
789
800
 
790
801
  async function runHostReport(args, directory) {
791
- const { values } = declared(args, ["--house", "--config"], 0, { exact: true });
792
- const house = await selectedHouse(values, directory);
793
- if (!house) throw new Error(HOST_USAGE);
802
+ const { values } = declared(args, ["--config"], 0, { exact: true });
803
+ const house = await storedHouse(directory);
794
804
  const observed = await reportHost({
795
805
  house,
796
806
  directory,
@@ -802,9 +812,8 @@ async function runHostReport(args, directory) {
802
812
  }
803
813
 
804
814
  async function runAgentsUpdate(args, directory) {
805
- const { values } = declared(args, ["--house", "--config"], 0, { exact: true });
806
- const house = await selectedHouse(values, directory);
807
- if (!house) throw new Error(AGENTS_USAGE);
815
+ const { values } = declared(args, ["--config"], 0, { exact: true });
816
+ const house = await storedHouse(directory);
808
817
  const { updated } = await updateAgents({
809
818
  house,
810
819
  directory,
@@ -830,10 +839,10 @@ async function runAgentsUpdate(args, directory) {
830
839
  }
831
840
 
832
841
  async function runAgentsSignIn(args, directory) {
833
- const { values, rest } = declared(args, ["--house", "--config"], 1, { exact: true });
834
- const house = await selectedHouse(values, directory);
842
+ const { values, rest } = declared(args, ["--config"], 1, { exact: true });
835
843
  const [kind] = rest;
836
- if (!house || !kind) throw new Error(AGENTS_USAGE);
844
+ if (!kind) throw new Error(AGENTS_USAGE);
845
+ const house = await storedHouse(directory);
837
846
  const { status, reported } = await signInAgent({
838
847
  house,
839
848
  directory,
@@ -850,19 +859,29 @@ async function runAgentsSignIn(args, directory) {
850
859
  }
851
860
 
852
861
  async function runWorkspaceHost(args, directory) {
853
- const { values } = declared(args, ["--house", "--config", "--passes"], 0, { exact: true });
854
- const house = await selectedHouse(values, directory);
855
- if (!house) throw new Error(WORKSPACE_USAGE);
862
+ const { values } = declared(args, ["--config", "--passes"], 0, { exact: true });
856
863
  const passes = values["--passes"] === undefined ? undefined : Number(values["--passes"]);
857
864
  if (passes !== undefined && (!Number.isSafeInteger(passes) || passes < 1)) {
858
865
  throw new Error(WORKSPACE_USAGE);
859
866
  }
860
- const served = await serveWorkspaces({
861
- house,
862
- directory,
863
- configuration: values["--config"] ?? join(directory, "kit.json"),
864
- passes,
865
- });
867
+ const house = await storedHouse(directory);
868
+ const cancellation = new AbortController();
869
+ const cancel = () => cancellation.abort();
870
+ process.once("SIGINT", cancel);
871
+ process.once("SIGTERM", cancel);
872
+ let served;
873
+ try {
874
+ served = await serveWorkspaces({
875
+ house,
876
+ directory,
877
+ configuration: values["--config"] ?? join(directory, "kit.json"),
878
+ passes,
879
+ signal: cancellation.signal,
880
+ });
881
+ } finally {
882
+ process.removeListener("SIGINT", cancel);
883
+ process.removeListener("SIGTERM", cancel);
884
+ }
866
885
  for (const name of served.reconciled) {
867
886
  write(`Stopped ${name}; House authorizes no running workspace for it.`);
868
887
  }
@@ -876,10 +895,24 @@ async function runWorkspaceHost(args, directory) {
876
895
  }
877
896
  }
878
897
 
898
+ async function runWorkspaceUpdate(args, directory) {
899
+ declared(args, [], 0, { exact: true });
900
+ const answered = await updateWorkspaces({
901
+ house: await storedHouse(directory),
902
+ configuration: join(directory, "kit.json"),
903
+ });
904
+ for (const { workspace, running } of answered.updated) {
905
+ write(running ? `Updated workspace ${workspace}.` : `Updated workspace ${workspace}; it stays stopped.`);
906
+ }
907
+ for (const { workspace, cause } of answered.unchanged) {
908
+ write(`Workspace ${workspace} is unchanged: ${cause}`);
909
+ }
910
+ if (answered.unchanged.length > 0) process.exitCode = 1;
911
+ }
912
+
879
913
  async function runTelegramConnect(args, directory) {
880
- const { values } = declared(args, ["--house"], 0, { exact: true });
881
- const house = await selectedHouse(values, directory);
882
- if (!house) throw new Error(TELEGRAM_USAGE);
914
+ declared(args, [], 0, { exact: true });
915
+ const house = await storedHouse(directory);
883
916
  const cancellation = new AbortController();
884
917
  const cancel = () => cancellation.abort();
885
918
  process.once("SIGINT", cancel);
@@ -897,9 +930,8 @@ async function runTelegramConnect(args, directory) {
897
930
  }
898
931
 
899
932
  async function runTelegramDisconnect(args, directory) {
900
- const { values } = declared(args, ["--house"], 0, { exact: true });
901
- const house = await selectedHouse(values, directory);
902
- if (!house) throw new Error(TELEGRAM_USAGE);
933
+ declared(args, [], 0, { exact: true });
934
+ const house = await storedHouse(directory);
903
935
  const cancellation = new AbortController();
904
936
  const cancel = () => cancellation.abort();
905
937
  process.once("SIGINT", cancel);
@@ -919,20 +951,25 @@ async function runTelegramDisconnect(args, directory) {
919
951
  }
920
952
 
921
953
  async function runTelegramServe(args, directory) {
922
- const { values } = declared(args, ["--house", "--passes"], 0, { exact: true });
923
- const house = await selectedHouse(values, directory);
924
- if (!house) throw new Error(TELEGRAM_USAGE);
954
+ const { values } = declared(args, ["--passes"], 0, { exact: true });
925
955
  const passes = values["--passes"] === undefined ? undefined : Number(values["--passes"]);
926
956
  if (passes !== undefined && (!Number.isSafeInteger(passes) || passes < 1)) {
927
957
  throw new Error(TELEGRAM_USAGE);
928
958
  }
959
+ const house = await storedHouse(directory);
929
960
  const cancellation = new AbortController();
930
961
  const cancel = () => cancellation.abort();
931
962
  process.once("SIGINT", cancel);
932
963
  process.once("SIGTERM", cancel);
933
964
  let served;
934
965
  try {
935
- served = await serveTelegramGateway({ house, directory, passes, signal: cancellation.signal });
966
+ served = await serveTelegramGateway({
967
+ house,
968
+ directory,
969
+ passes,
970
+ signal: cancellation.signal,
971
+ refused: (failure) => write(refusedPassLine(failure)),
972
+ });
936
973
  } finally {
937
974
  process.removeListener("SIGINT", cancel);
938
975
  process.removeListener("SIGTERM", cancel);
@@ -952,20 +989,23 @@ async function runTelegramServe(args, directory) {
952
989
  `File send ${settled.upload} settled ${settled.outcome}${settled.reason === null ? "" : ` (${settled.reason})`}.`,
953
990
  );
954
991
  }
955
- for (const failure of served.failures) {
956
- write(
957
- `The ${failure.activity} pass${failure.item === null ? "" : ` for ${failure.item}`} could not finish (${failure.code}).`,
958
- );
959
- }
992
+ for (const failure of served.failures) write(refusedPassLine(failure));
960
993
  if (served.failures.length > 0) process.exitCode = 1;
961
994
  }
962
995
 
996
+ function refusedPassLine(failure) {
997
+ return `The ${failure.activity} pass${failure.item === null ? "" : ` for ${failure.item}`} could not finish (${failure.code}).`;
998
+ }
999
+
963
1000
  async function main() {
964
1001
  const [command, ...args] = process.argv.slice(2);
965
1002
  const directory = process.env.HOUSE_KIT_HOME ?? join(homedir(), ".house-kit");
966
1003
  if (command === "login") return runLogin(args, directory);
967
1004
  if (command === "credential" && args[0] === "check") return runCredentialCheck(args.slice(1), directory);
968
- if (command === "resident") return runResidentCommand(args, directory);
1005
+ if (command === "resident") {
1006
+ if (args[0] === "drain") return runResidentDrain(args.slice(1), directory);
1007
+ return runResidentCommand(args, directory);
1008
+ }
969
1009
  if (command === "setup") return runSetup(args, directory);
970
1010
  if (command === "apply") return runApply(args, directory);
971
1011
  if (command === "worker") {
@@ -1002,6 +1042,7 @@ async function main() {
1002
1042
  if (command === "workspace") {
1003
1043
  const [action, ...rest] = args;
1004
1044
  if (action === "host") return runWorkspaceHost(rest, directory);
1045
+ if (action === "update") return runWorkspaceUpdate(rest, directory);
1005
1046
  throw new Error(WORKSPACE_USAGE);
1006
1047
  }
1007
1048
  if (command === "telegram") {