run402 4.69.8 → 4.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/gitvault-surface.json +5 -1
- package/lib/command-manifest.mjs +2 -0
- package/lib/gitvault-capabilities.mjs +6 -0
- package/lib/harness-context.mjs +50 -0
- package/lib/messages.mjs +83 -1
- package/lib/repos.mjs +313 -9
- package/lib/rooms-context.mjs +23 -13
- package/package.json +1 -1
- package/sdk/dist/errors.d.ts +1 -1
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/namespaces/gitvault.d.ts +223 -0
- package/sdk/dist/namespaces/gitvault.d.ts.map +1 -1
- package/sdk/dist/namespaces/gitvault.js +609 -2
- package/sdk/dist/namespaces/gitvault.js.map +1 -1
- package/sdk/dist/namespaces/rooms.d.ts +20 -1
- package/sdk/dist/namespaces/rooms.d.ts.map +1 -1
- package/sdk/dist/namespaces/rooms.js +93 -0
- package/sdk/dist/namespaces/rooms.js.map +1 -1
- package/sdk/dist/namespaces/rooms.types.d.ts +73 -0
- package/sdk/dist/namespaces/rooms.types.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-address.d.ts +1 -0
- package/sdk/dist/node/gitvault-address.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-address.js +5 -1
- package/sdk/dist/node/gitvault-address.js.map +1 -1
- package/sdk/dist/node/gitvault-handoff.d.ts +109 -31
- package/sdk/dist/node/gitvault-handoff.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-handoff.js +264 -137
- package/sdk/dist/node/gitvault-handoff.js.map +1 -1
- package/sdk/dist/node/gitvault-keystore.d.ts +2 -2
- package/sdk/dist/node/gitvault-keystore.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-restore.d.ts +19 -0
- package/sdk/dist/node/gitvault-restore.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-restore.js +59 -2
- package/sdk/dist/node/gitvault-restore.js.map +1 -1
- package/sdk/dist/node/index.d.ts +4 -4
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +9 -5
- package/sdk/dist/node/index.js.map +1 -1
package/README.md
CHANGED
|
@@ -68,10 +68,12 @@ run402 repos list --org org_1a2b3c
|
|
|
68
68
|
run402 repos delete --project prj_xyz --force # refuses without --force while the vault holds generations
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
A hosted git remote, encrypted before it leaves the machine — no deploy, no manifest, no app. `origin` is claimed additively — an existing `origin` is never touched, the run402 remote falls back to `run402` instead. Every mutating `repos` verb (`create`, `rename`, `delete`, `snapshot`, `policy`, `mirror`, `gc`, `handoff`, `resume`) is CLI/OpenClaw-only by design — no MCP tool exists or will exist for them (one-shot recovery receipts, immutable generations, irreversible delete, single-use bearer keys). Three READ-ONLY tools do exist — `repos_view`, `repos_list_heads`, `repos_fsck` — teaching only `repos` spellings. See `run402 repos --help` for the full tiered surface (common: `create`/`view`/`list`; occasional: `snapshot`/`mirror`/`recover`/`handoff`/`resume`; lifecycle: `rename`/`delete`; maintenance: `fsck`/`gc`/`access`/`policy`), and the CLI reference's `repos` section for the terminal-loss statement and the progressive backup warning.
|
|
71
|
+
A hosted git remote, encrypted before it leaves the machine — no deploy, no manifest, no app. `origin` is claimed additively — an existing `origin` is never touched, the run402 remote falls back to `run402` instead. Every mutating `repos` verb (`create`, `rename`, `delete`, `snapshot`, `policy`, `mirror`, `gc`, `handoff`, `resume`, `invite`, `join`) is CLI/OpenClaw-only by design — no MCP tool exists or will exist for them (one-shot recovery receipts, immutable generations, irreversible delete, single-use bearer keys). Three READ-ONLY tools do exist — `repos_view`, `repos_list_heads`, `repos_fsck` — teaching only `repos` spellings. See `run402 repos --help` for the full tiered surface (common: `create`/`view`/`list`; occasional: `snapshot`/`mirror`/`recover`/`handoff`/`resume`/`invite`/`join`; lifecycle: `rename`/`delete`; maintenance: `fsck`/`gc`/`access`/`policy`), and the CLI reference's `repos` section for the terminal-loss statement and the progressive backup warning.
|
|
72
72
|
|
|
73
73
|
**Handoff / resume.** `run402 repos handoff [--note-file <path>]` captures the actual working tree (staged/unstaged/untracked, like `git stash push -u`) into the vault and mints a single-use bearer key (`kgh1_…`, printed to stdout exactly once); `run402 repos resume <kgh1_…>` claims it on another machine, clones fresh, and reapplies the exact dirty state with `git stash apply --index`. No shared keystore, no shared allowance — the key is the whole handoff. On a wallet with no active tier, `resume` first folds the same cold-start chain `create` does (allowance → faucet → one x402 prototype payment); `--no-init` opts out, and the claim itself never waits on it. Sensitive untracked files (`.env`, `*.pem`, SSH/AWS/GPG dirs, …) are excluded from capture by default (`--include-sensitive <glob>` re-admits one). See the reference `run402.com/llms-cli.txt` for the full note-schema and flag reference.
|
|
74
74
|
|
|
75
|
+
**Invite / join.** The second claim kind beside handoff/resume: a Handoff passes the work on and the sender stops; an Invite grows the team while the sender keeps working. `run402 repos invite [--room <key>] [--note-file <path>]` captures the checkpoint exactly like `handoff` — the inviter's own worktree, index, branch, refs, and access are all untouched — registers the inviter's presence in a coordination room (the project's default room, or `--room <key>` for a named org room), mints a single-use bearer key (`kgi1_…`, printed to stdout exactly once), and posts ONE room message naming the checkpoint (never the key). `run402 repos join <kgi1_…>` folds the SAME cold-start chain `resume` does before the claim, clones fresh, restores the exact dirty state, pins the invite's room locally, registers its own presence, posts ONE arrival message, and reports the inviter (name, labels, liveness), the room's other live presences, and the last few messages. `run402 messages wait` is the room's blocking read from there — the agent's ear, never an error on silence. Neither `invite` nor `join` has an MCP tool, same reasoning as `handoff`/`resume`.
|
|
76
|
+
|
|
75
77
|
**Named addressing.** `run402 org slug <slug>` (owner-only, small one-time fee) claims an org's globally-unique, address-form slug, after which `run402::<slug>/<name>` addresses any repo under it — `git push` to a name that doesn't exist yet push-to-creates it. `run402 repos rename <name> [--project <id>]` claims the per-org-unique `<name>` half explicitly (no fee); `repos create` claims one automatically, best-effort, when the org already has a slug. Also CLI/SDK-only — no MCP tool.
|
|
76
78
|
|
|
77
79
|
### Allowance
|
package/gitvault-surface.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"surface_version": "4.
|
|
2
|
+
"surface_version": "4.70.0",
|
|
3
3
|
"verbs": [
|
|
4
4
|
"repos create",
|
|
5
5
|
"repos list",
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
"repos snapshot",
|
|
10
10
|
"repos handoff",
|
|
11
11
|
"repos resume",
|
|
12
|
+
"repos invite",
|
|
13
|
+
"repos join",
|
|
12
14
|
"repos policy",
|
|
13
15
|
"repos mirror",
|
|
14
16
|
"repos fsck",
|
|
@@ -77,6 +79,8 @@
|
|
|
77
79
|
"snapshot_dirty_default": "refuse",
|
|
78
80
|
"handoff_live": true,
|
|
79
81
|
"handoff_dirty_default": "capture",
|
|
82
|
+
"invite_live": true,
|
|
83
|
+
"join_live": true,
|
|
80
84
|
"remote_schemes": [
|
|
81
85
|
"run402",
|
|
82
86
|
"kygit"
|
package/lib/command-manifest.mjs
CHANGED
|
@@ -296,6 +296,8 @@ export const COMMAND_MANIFEST = [
|
|
|
296
296
|
// (a real membership mutation) and writes into a fresh working tree.
|
|
297
297
|
{ path: ["repos", "handoff"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "captures the cwd git working tree and mints a single-use bearer key against a live vault — never run against the gate's own checkout" },
|
|
298
298
|
{ path: ["repos", "resume"], positionals: [p("key")], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["kgh1_0000000000000000000000000000000000000000000000000000000000000000"], runStyle: "sub", skipBehavioral: "claims a live handoff (a real org-membership mutation) and clones a fresh working tree" },
|
|
299
|
+
{ path: ["repos", "invite"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "captures the cwd git working tree and mints a single-use bearer key against a live vault, posting a real room message — never run against the gate's own checkout" },
|
|
300
|
+
{ path: ["repos", "join"], positionals: [p("key")], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["kgi1_0000000000000000000000000000000000000000000000000000000000000000"], runStyle: "sub", skipBehavioral: "folds the cold-start chain, claims a live invite (a real org-membership mutation + one x402 payment), and clones a fresh working tree" },
|
|
299
301
|
{ path: ["repos", "policy"], positionals: [p("repos_policy")], projectScoped: true, legacyPositionalProject: false, minimalArgs: ["required"], runStyle: "sub", skipBehavioral: "owner + step-up mutation of the live project's activation policy" },
|
|
300
302
|
// ONE flag-driven verb; `<destination>` is a real attribute (not a
|
|
301
303
|
// sub-verb literal), so this stays a single manifest entry.
|
|
@@ -53,6 +53,12 @@ export const GITVAULT_CAPABILITIES = {
|
|
|
53
53
|
// from `snapshot_dirty_default` above) — two verbs, two defaults, one
|
|
54
54
|
// capture path (design D1).
|
|
55
55
|
handoff_dirty_default: "capture",
|
|
56
|
+
// kygit-invite (design D10): `repos invite` / `repos join` mint and claim
|
|
57
|
+
// a second claim kind — a real single-use bearer key against a live
|
|
58
|
+
// gateway route, admitting the recipient to a shared coordination room
|
|
59
|
+
// while the inviter keeps working.
|
|
60
|
+
invite_live: true,
|
|
61
|
+
join_live: true,
|
|
56
62
|
// Both remote door spellings resolve identically (design D8):
|
|
57
63
|
// `run402::` (canonical, plumbing) and `kygit::` (rendered by
|
|
58
64
|
// `@kychee/kygit` once it sets `RUN402_REMOTE_SCHEME=kygit`).
|
package/lib/harness-context.mjs
CHANGED
|
@@ -23,6 +23,8 @@ import { randomBytes } from "node:crypto";
|
|
|
23
23
|
|
|
24
24
|
export const SESSION_KEY_OVERRIDE_ENV = "RUN402_SESSION_KEY";
|
|
25
25
|
export const TASK_FROM_TITLE_OPT_OUT_ENV = "RUN402_NO_TASK_FROM_TITLE";
|
|
26
|
+
export const PROGRAM_OVERRIDE_ENV = "RUN402_PROGRAM";
|
|
27
|
+
export const MODEL_OVERRIDE_ENV = "RUN402_MODEL";
|
|
26
28
|
const SESSION_KEY_CACHE_RELATIVE_PATH = ".run402/session-key.json";
|
|
27
29
|
const CLAUDE_SESSIONS_DIR_PARTS = ["Library", "Application Support", "Claude", "claude-code-sessions"];
|
|
28
30
|
const CODEX_STATE_DB_PARTS = [".codex", "state_5.sqlite"];
|
|
@@ -81,6 +83,54 @@ export function resolveSessionKey({
|
|
|
81
83
|
return { key: generated, source: "generated" };
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Best-effort presence labels sourced from the harness this CLI invocation
|
|
88
|
+
* is running under (kygit-invite design D8) — `program`/`model` on
|
|
89
|
+
* `rooms join`, the implicit registration inside `messages send`, and every
|
|
90
|
+
* `repos invite`/`repos join` presence registration. Precedence: explicit
|
|
91
|
+
* env overrides first (`RUN402_PROGRAM`/`RUN402_MODEL`), then `program`
|
|
92
|
+
* inferred from the SAME harness signals {@link resolveSessionKey} already
|
|
93
|
+
* trusts (`CLAUDE_CODE_SESSION_ID` or `CLAUDECODE` -> `"claude-code"`;
|
|
94
|
+
* `CODEX_THREAD_ID` -> `"codex"`). `model` has no harness-exposed signal to
|
|
95
|
+
* infer from today (open question in kygit-invite design.md) — it is
|
|
96
|
+
* ALWAYS env-override-or-null, never guessed from `program`. Null stays
|
|
97
|
+
* null in both fields: a placeholder label would be a Faithful breach.
|
|
98
|
+
*/
|
|
99
|
+
export function resolveHarnessLabels({ env = process.env } = {}) {
|
|
100
|
+
const programOverride = env[PROGRAM_OVERRIDE_ENV]?.trim();
|
|
101
|
+
const modelOverride = env[MODEL_OVERRIDE_ENV]?.trim();
|
|
102
|
+
let program = programOverride || null;
|
|
103
|
+
if (!program) {
|
|
104
|
+
if (env.CLAUDE_CODE_SESSION_ID?.trim() || env.CLAUDECODE?.trim()) {
|
|
105
|
+
program = "claude-code";
|
|
106
|
+
} else if (env.CODEX_THREAD_ID?.trim()) {
|
|
107
|
+
program = "codex";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const model = modelOverride || null;
|
|
111
|
+
return { program, model };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Persist a session key into ANOTHER checkout's cache. `repos join` uses it
|
|
116
|
+
* so the freshly cloned checkout resolves the SAME session identity the join
|
|
117
|
+
* registered its presence under (kygit-invite design D5), instead of
|
|
118
|
+
* generating a stranger's key on the first `messages wait` run there. Only
|
|
119
|
+
* meaningful for the generated-key path — a harness-provided id is found in
|
|
120
|
+
* the environment first regardless of cwd, so callers skip this for those
|
|
121
|
+
* sources. Best-effort, never throws; returns whether the write landed.
|
|
122
|
+
*/
|
|
123
|
+
export function persistSessionKey(cwd, key, { mkdirSyncImpl = mkdirSync, writeFileSyncImpl = writeFileSync } = {}) {
|
|
124
|
+
if (typeof key !== "string" || !key) return false;
|
|
125
|
+
try {
|
|
126
|
+
mkdirSyncImpl(join(cwd, ".run402"), { recursive: true });
|
|
127
|
+
writeFileSyncImpl(join(cwd, SESSION_KEY_CACHE_RELATIVE_PATH), `${JSON.stringify({ session_key: key }, null, 2)}\n`);
|
|
128
|
+
return true;
|
|
129
|
+
} catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
84
134
|
/**
|
|
85
135
|
* Bounded-depth search for a file named `filename` under `root`. Stops at
|
|
86
136
|
* the first match, or after MAX_TITLE_SEARCH_DIRS directories visited,
|
package/lib/messages.mjs
CHANGED
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
getRoomState,
|
|
34
34
|
updateRoomState,
|
|
35
35
|
} from "./rooms-context.mjs";
|
|
36
|
-
import { resolveTaskLabel } from "./harness-context.mjs";
|
|
36
|
+
import { resolveTaskLabel, resolveHarnessLabels } from "./harness-context.mjs";
|
|
37
37
|
|
|
38
38
|
export const IMPORTANCE = ["normal", "high"];
|
|
39
39
|
|
|
@@ -44,6 +44,7 @@ const HELP = `run402 messages — room-visible messages between agents
|
|
|
44
44
|
Usage:
|
|
45
45
|
run402 messages send <body> [--to <names>] [--ack] [--thread <id>]
|
|
46
46
|
run402 messages list [--unread] [--cursor <mcr_...>] [--thread <id>]
|
|
47
|
+
run402 messages wait [--addressed-to me] [--thread <id>] [--timeout <s>] [--cursor <mcr_...>]
|
|
47
48
|
run402 messages get <message_id>
|
|
48
49
|
run402 messages ack <message_id>
|
|
49
50
|
|
|
@@ -62,6 +63,17 @@ Notes:
|
|
|
62
63
|
your existing presence (no rename) when one is resolvable, and --task —
|
|
63
64
|
or, if omitted, your harness's own thread title — refreshes what the room
|
|
64
65
|
sees you working on.
|
|
66
|
+
- wait is the session's EAR: it blocks until a message lands past this
|
|
67
|
+
checkout's stored cursor (or --cursor), using the gateway's held read
|
|
68
|
+
when available and plain polling when an older gateway answers at once —
|
|
69
|
+
the output shape never changes. Default timeout 120s, max 600s (a
|
|
70
|
+
coding harness's own shell-call limit — a wait that outlives the tool
|
|
71
|
+
call is a wait the agent never hears). Silence is an answer, never an
|
|
72
|
+
error: it exits 0 with an empty messages[] and \`live_presences[]\`
|
|
73
|
+
naming who is still in the room, so "did they leave" needs no second
|
|
74
|
+
call. The returned cursor is persisted, exactly like \`list\` — the next
|
|
75
|
+
wait resumes right after the last message seen. Join the room first
|
|
76
|
+
with \`run402 rooms join\` if you want a chosen name.
|
|
65
77
|
- The room itself — arriving, leaving, seeing who is live — is \`run402 rooms\`.
|
|
66
78
|
`;
|
|
67
79
|
|
|
@@ -118,6 +130,10 @@ async function send(args) {
|
|
|
118
130
|
});
|
|
119
131
|
try {
|
|
120
132
|
const { task } = await resolveTaskLabel({ explicitTask: flagValue(a, "--task") });
|
|
133
|
+
// kygit-invite design D8: the implicit presence-creation path (no
|
|
134
|
+
// presenceId cached yet) carries harness-derived labels too — never
|
|
135
|
+
// guessed, null stays null.
|
|
136
|
+
const { program, model } = resolveHarnessLabels();
|
|
121
137
|
const result = await withPresenceRetry(room.orgId, room.roomKey, (presenceId, sessionKey) =>
|
|
122
138
|
getSdk().rooms.sendMessage(room.orgId, room.roomKey, {
|
|
123
139
|
body: positionals[0],
|
|
@@ -131,6 +147,8 @@ async function send(args) {
|
|
|
131
147
|
sessionKey,
|
|
132
148
|
requestedName: flagValue(a, "--name") ?? undefined,
|
|
133
149
|
task: task ?? undefined,
|
|
150
|
+
program: program ?? undefined,
|
|
151
|
+
model: model ?? undefined,
|
|
134
152
|
}),
|
|
135
153
|
{ name: flagValue(a, "--name"), task });
|
|
136
154
|
rememberPresence(room.orgId, room.roomKey, result.sender_presence, flagValue(a, "--name"));
|
|
@@ -177,6 +195,66 @@ async function list(args) {
|
|
|
177
195
|
}
|
|
178
196
|
}
|
|
179
197
|
|
|
198
|
+
/**
|
|
199
|
+
* `run402 messages wait` — the agent's ear (kygit-invite design D7). Blocks
|
|
200
|
+
* until a matching message lands past this checkout's stored cursor, or the
|
|
201
|
+
* timeout elapses, using the shared `rooms.waitForMessages` (held read when
|
|
202
|
+
* the gateway supports it, bounded polling otherwise). Stdout is ALWAYS one
|
|
203
|
+
* JSON document — silence is a normal, exit-0 answer, never a thrown error.
|
|
204
|
+
*/
|
|
205
|
+
async function wait(args) {
|
|
206
|
+
const a = normalizeArgv(args);
|
|
207
|
+
const valueFlags = [...ROOM_FLAGS, "--thread", "--timeout", "--cursor", "--addressed-to"];
|
|
208
|
+
assertKnownFlags(a, [...valueFlags, "--help", "-h"], valueFlags);
|
|
209
|
+
requirePositionalCount(positionalArgs(a, valueFlags), valueFlags, {
|
|
210
|
+
min: 0, max: 0, command: "run402 messages wait", missing: "",
|
|
211
|
+
});
|
|
212
|
+
const addressedTo = flagValue(a, "--addressed-to");
|
|
213
|
+
if (addressedTo != null) assertAllowedValue(addressedTo, ["me"], "--addressed-to");
|
|
214
|
+
const timeoutRaw = flagValue(a, "--timeout");
|
|
215
|
+
// Default 120s, max 600s: a Claude Code shell call dies at 600s, so a wait
|
|
216
|
+
// that outlives the tool call is a wait the agent never hears (design D7).
|
|
217
|
+
const timeoutSeconds = timeoutRaw != null ? parseIntegerFlag("--timeout", timeoutRaw, { min: 1, max: 600 }) : 120;
|
|
218
|
+
const room = await resolveRoom({
|
|
219
|
+
org: flagValue(a, "--org"), room: flagValue(a, "--room"), project: flagValue(a, "--project"),
|
|
220
|
+
});
|
|
221
|
+
const stored = getRoomState(room.orgId, room.roomKey).cursor;
|
|
222
|
+
const cursor = flagValue(a, "--cursor") ?? (typeof stored === "string" ? stored : undefined);
|
|
223
|
+
try {
|
|
224
|
+
const { task } = await resolveTaskLabel({});
|
|
225
|
+
const me = await ensurePresence(room, { task });
|
|
226
|
+
console.error(`waiting in ${room.roomKey} as ${me.name} (timeout ${timeoutSeconds}s)…`);
|
|
227
|
+
const result = await getSdk().rooms.waitForMessages(room.orgId, room.roomKey, {
|
|
228
|
+
...(cursor !== undefined ? { cursor } : {}),
|
|
229
|
+
threadId: flagValue(a, "--thread") ?? undefined,
|
|
230
|
+
addressedTo: addressedTo === "me" ? "me" : undefined,
|
|
231
|
+
presenceId: me.presence_id,
|
|
232
|
+
timeoutMs: timeoutSeconds * 1000,
|
|
233
|
+
});
|
|
234
|
+
if (typeof result.cursor === "string") {
|
|
235
|
+
updateRoomState(room.orgId, room.roomKey, { cursor: result.cursor });
|
|
236
|
+
}
|
|
237
|
+
console.log(JSON.stringify({
|
|
238
|
+
messages: result.messages,
|
|
239
|
+
cursor: result.cursor,
|
|
240
|
+
has_more: result.has_more,
|
|
241
|
+
settled: result.settled,
|
|
242
|
+
waited_ms: result.waited_ms,
|
|
243
|
+
live_presences: result.live_presences,
|
|
244
|
+
}, null, 2));
|
|
245
|
+
if (result.settled) {
|
|
246
|
+
console.error(`${result.messages.length} message(s) arrived.`);
|
|
247
|
+
} else {
|
|
248
|
+
const live = result.live_presences ?? [];
|
|
249
|
+
console.error(live.length > 0
|
|
250
|
+
? `silence — still live: ${live.map((p) => p.name).join(", ")}.`
|
|
251
|
+
: "silence — nobody else is currently live in this room.");
|
|
252
|
+
}
|
|
253
|
+
} catch (err) {
|
|
254
|
+
reportSdkError(err);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
180
258
|
async function get(args) {
|
|
181
259
|
const a = normalizeArgv(args);
|
|
182
260
|
assertKnownFlags(a, [...ROOM_FLAGS, "--help", "-h"], ROOM_FLAGS);
|
|
@@ -232,6 +310,10 @@ export async function run(sub, args) {
|
|
|
232
310
|
await list(argv);
|
|
233
311
|
break;
|
|
234
312
|
}
|
|
313
|
+
case "wait": {
|
|
314
|
+
await wait(argv);
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
235
317
|
case "get": {
|
|
236
318
|
await get(argv);
|
|
237
319
|
break;
|