run402 4.70.9 → 4.71.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/gitvault-surface.json +1 -1
- package/lib/cold-start.mjs +33 -7
- package/lib/command-manifest.mjs +8 -1
- package/lib/org-context.mjs +8 -2
- package/lib/rooms-context.mjs +14 -3
- package/lib/rooms.mjs +299 -2
- package/lib/sdk-errors.mjs +25 -0
- package/package.json +1 -1
- package/sdk/dist/namespaces/rooms.d.ts +53 -1
- package/sdk/dist/namespaces/rooms.d.ts.map +1 -1
- package/sdk/dist/namespaces/rooms.js +130 -0
- package/sdk/dist/namespaces/rooms.js.map +1 -1
- package/sdk/dist/namespaces/rooms.types.d.ts +94 -0
- package/sdk/dist/namespaces/rooms.types.d.ts.map +1 -1
- package/sdk/dist/node/bearer-claim-key.d.ts +126 -0
- package/sdk/dist/node/bearer-claim-key.d.ts.map +1 -0
- package/sdk/dist/node/bearer-claim-key.js +209 -0
- package/sdk/dist/node/bearer-claim-key.js.map +1 -0
- package/sdk/dist/node/gitvault-address.d.ts +19 -0
- package/sdk/dist/node/gitvault-address.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-address.js +20 -0
- package/sdk/dist/node/gitvault-address.js.map +1 -1
- package/sdk/dist/node/gitvault-handoff.d.ts +3 -95
- package/sdk/dist/node/gitvault-handoff.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-handoff.js +9 -122
- package/sdk/dist/node/gitvault-handoff.js.map +1 -1
- package/sdk/dist/node/index.d.ts +3 -1
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +8 -1
- package/sdk/dist/node/index.js.map +1 -1
- package/sdk/dist/node/paid-fetch.d.ts +8 -0
- package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
- package/sdk/dist/node/paid-fetch.js +41 -1
- package/sdk/dist/node/paid-fetch.js.map +1 -1
package/gitvault-surface.json
CHANGED
package/lib/cold-start.mjs
CHANGED
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
* untouched; this module does not replace it, only the one path `repos
|
|
15
15
|
* create` needs when it discovers there is no tier and nothing else has
|
|
16
16
|
* set one up yet.
|
|
17
|
+
*
|
|
18
|
+
* add-room-invite design D9 splits the funded-wallet half (allowance →
|
|
19
|
+
* faucet-if-empty → brief settlement poll) out as {@link ensureFundedWallet}
|
|
20
|
+
* — `rooms join <key>` needs exactly that half, with NO tier purchase and NO
|
|
21
|
+
* project creation (the claim itself is the one x402 payment that funds the
|
|
22
|
+
* onboarding). {@link foldColdStartChain} is now their composition —
|
|
23
|
+
* `ensureFundedWallet` followed by the tier step — so every EXISTING caller
|
|
24
|
+
* (`repos create`/`resume`/`join`) is unchanged.
|
|
17
25
|
*/
|
|
18
26
|
import { readAllowance, saveAllowance } from "./config.mjs";
|
|
19
27
|
import { getSdk } from "./sdk.mjs";
|
|
@@ -22,13 +30,16 @@ const USDC_ABI = [{ name: "balanceOf", type: "function", stateMutability: "view"
|
|
|
22
30
|
const USDC_SEPOLIA = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
|
|
23
31
|
|
|
24
32
|
/**
|
|
33
|
+
* Allowance → faucet-if-empty → brief settlement poll. NO tier purchase, no
|
|
34
|
+
* project creation — the caller decides what (if anything) to pay for next.
|
|
35
|
+
*
|
|
25
36
|
* @param {(line: string) => void} announce Called once per step, so the
|
|
26
37
|
* caller can print each one it took (client-surface spec: "announcing
|
|
27
38
|
* each step").
|
|
28
|
-
* @returns {Promise<{allowance_created: boolean, faucet_requested: boolean,
|
|
39
|
+
* @returns {Promise<{allowance_created: boolean, faucet_requested: boolean, address: string}>}
|
|
29
40
|
*/
|
|
30
|
-
export async function
|
|
31
|
-
const out = { allowance_created: false, faucet_requested: false,
|
|
41
|
+
export async function ensureFundedWallet(announce = () => {}) {
|
|
42
|
+
const out = { allowance_created: false, faucet_requested: false, address: "" };
|
|
32
43
|
|
|
33
44
|
let allowance = readAllowance();
|
|
34
45
|
if (!allowance) {
|
|
@@ -40,6 +51,7 @@ export async function foldColdStartChain(announce = () => {}) {
|
|
|
40
51
|
out.allowance_created = true;
|
|
41
52
|
announce(`allowance created: ${allowance.address}`);
|
|
42
53
|
}
|
|
54
|
+
out.address = allowance.address;
|
|
43
55
|
|
|
44
56
|
const { createPublicClient, http } = await import("viem");
|
|
45
57
|
const { baseSepolia } = await import("viem/chains");
|
|
@@ -48,7 +60,7 @@ export async function foldColdStartChain(announce = () => {}) {
|
|
|
48
60
|
try {
|
|
49
61
|
balance = Number(await client.readContract({ address: USDC_SEPOLIA, abi: USDC_ABI, functionName: "balanceOf", args: [allowance.address] }));
|
|
50
62
|
} catch {
|
|
51
|
-
/* an RPC hiccup here is not fatal — the
|
|
63
|
+
/* an RPC hiccup here is not fatal — the payment below will surface a real failure if the balance really is zero */
|
|
52
64
|
}
|
|
53
65
|
if (balance === 0) {
|
|
54
66
|
announce("balance is 0 — requesting the testnet faucet");
|
|
@@ -70,8 +82,22 @@ export async function foldColdStartChain(announce = () => {}) {
|
|
|
70
82
|
saveAllowance({ ...allowance, funded: true, lastFaucet: new Date().toISOString() });
|
|
71
83
|
}
|
|
72
84
|
|
|
73
|
-
announce("subscribing to the prototype tier (one x402 testnet payment, perpetual)");
|
|
74
|
-
out.tier = await getSdk().tier.set("prototype");
|
|
75
|
-
announce(`prototype tier active${out.tier?.status === "already_active" ? " (already active)" : ""}`);
|
|
76
85
|
return out;
|
|
77
86
|
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* `ensureFundedWallet` + the tier step. Unchanged from before the split —
|
|
90
|
+
* every existing caller (`repos create`/`resume`/`join`) keeps working with
|
|
91
|
+
* no edits.
|
|
92
|
+
*
|
|
93
|
+
* @param {(line: string) => void} announce Called once per step.
|
|
94
|
+
* @returns {Promise<{allowance_created: boolean, faucet_requested: boolean, tier: object|null}>}
|
|
95
|
+
*/
|
|
96
|
+
export async function foldColdStartChain(announce = () => {}) {
|
|
97
|
+
const funded = await ensureFundedWallet(announce);
|
|
98
|
+
|
|
99
|
+
announce("subscribing to the prototype tier (one x402 testnet payment, perpetual)");
|
|
100
|
+
const tier = await getSdk().tier.set("prototype");
|
|
101
|
+
announce(`prototype tier active${tier?.status === "already_active" ? " (already active)" : ""}`);
|
|
102
|
+
return { allowance_created: funded.allowance_created, faucet_requested: funded.faucet_requested, tier };
|
|
103
|
+
}
|
package/lib/command-manifest.mjs
CHANGED
|
@@ -257,7 +257,14 @@ export const COMMAND_MANIFEST = [
|
|
|
257
257
|
{ path: ["subscriptions", "add"], positionals: [], projectScoped: false, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
|
|
258
258
|
{ path: ["subscriptions", "list"], positionals: [], projectScoped: false, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
|
|
259
259
|
{ path: ["subscriptions", "rm"], positionals: [p("subscription_id")], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["r_1"], runStyle: "sub" },
|
|
260
|
-
|
|
260
|
+
// add-room-invite design D9: `rooms join` is one verb, two forms — no
|
|
261
|
+
// positional registers a presence (the behaviorally-tested path, unchanged
|
|
262
|
+
// by the optional positional below since minimalArgs stays empty); a
|
|
263
|
+
// `kri1_…` positional claims a room seat first (a real x402 payment + an
|
|
264
|
+
// org-membership mutation), so that form is never behaviorally exercised
|
|
265
|
+
// here — the same reasoning `repos join`'s own row states.
|
|
266
|
+
{ path: ["rooms", "join"], positionals: [p("key", { required: false })], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
|
|
267
|
+
{ path: ["rooms", "invite"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "mints a single-use bearer key from a live room, registering the inviter's presence and posting a real room message — never run against the gate's own checkout" },
|
|
261
268
|
{ path: ["rooms", "leave"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
|
|
262
269
|
{ path: ["messages", "send"], positionals: [p("body")], projectScoped: true, legacyPositionalProject: false, minimalArgs: ["hello"], runStyle: "sub" },
|
|
263
270
|
{ path: ["messages", "list"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
|
package/lib/org-context.mjs
CHANGED
|
@@ -98,13 +98,19 @@ function orgFromRoomEnv(env) {
|
|
|
98
98
|
* best-effort, gracefully degrading like `findBindingKey`: no repository,
|
|
99
99
|
* no pin, or a shape-invalid value all answer `null` rather than throwing,
|
|
100
100
|
* so a bare directory or a checkout with no gitvault remote costs nothing.
|
|
101
|
+
* Checks a real vault checkout's pin first (gated on `r402.repoId`), then
|
|
102
|
+
* falls back to the bare org/room pin a room-only `rooms join <key>` writes
|
|
103
|
+
* in a directory with no vault at all (add-room-invite design D10).
|
|
101
104
|
*/
|
|
102
105
|
async function readGitvaultPinnedOrgId(cwd) {
|
|
103
106
|
try {
|
|
104
|
-
const { readPinnedGitvaultRepo } = await import("#sdk/node");
|
|
107
|
+
const { readPinnedGitvaultRepo, readPinnedRoomBinding } = await import("#sdk/node");
|
|
105
108
|
const pinned = await readPinnedGitvaultRepo(cwd);
|
|
106
109
|
const orgId = trimmed(pinned?.org_id);
|
|
107
|
-
|
|
110
|
+
if (orgId && ORG_ID_RE.test(orgId)) return orgId;
|
|
111
|
+
const bare = await readPinnedRoomBinding(cwd);
|
|
112
|
+
const bareOrgId = trimmed(bare?.org_id);
|
|
113
|
+
return bareOrgId && ORG_ID_RE.test(bareOrgId) ? bareOrgId : null;
|
|
108
114
|
} catch {
|
|
109
115
|
return null;
|
|
110
116
|
}
|
package/lib/rooms-context.mjs
CHANGED
|
@@ -36,13 +36,24 @@ import { describeRejectedValue } from "../core-dist/redact.js";
|
|
|
36
36
|
export const ROOM_ENV = "RUN402_ROOM";
|
|
37
37
|
export const PRESENCE_ENV = "RUN402_PRESENCE_ID";
|
|
38
38
|
|
|
39
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* `r402.room` from `cwd`'s LOCAL git config (kygit-handoff design D10) —
|
|
41
|
+
* best-effort, `null` on any absence or failure. Checks a real vault
|
|
42
|
+
* checkout's pin first (`readPinnedGitvaultRepo`, gated on `r402.repoId`
|
|
43
|
+
* being present), then falls back to the bare room/org pin `rooms join
|
|
44
|
+
* <key>` writes in a directory with NO vault at all (add-room-invite design
|
|
45
|
+
* D10) — `readPinnedGitvaultRepo` would otherwise see that pin as absent,
|
|
46
|
+
* since it never sets `r402.repoId`.
|
|
47
|
+
*/
|
|
40
48
|
async function readGitvaultPinnedRoom(cwd) {
|
|
41
49
|
try {
|
|
42
|
-
const { readPinnedGitvaultRepo } = await import("#sdk/node");
|
|
50
|
+
const { readPinnedGitvaultRepo, readPinnedRoomBinding } = await import("#sdk/node");
|
|
43
51
|
const pinned = await readPinnedGitvaultRepo(cwd);
|
|
44
52
|
const room = typeof pinned?.room === "string" ? pinned.room.trim() : "";
|
|
45
|
-
|
|
53
|
+
if (room.length > 0) return room;
|
|
54
|
+
const bare = await readPinnedRoomBinding(cwd);
|
|
55
|
+
const bareRoom = typeof bare?.room === "string" ? bare.room.trim() : "";
|
|
56
|
+
return bareRoom.length > 0 ? bareRoom : null;
|
|
46
57
|
} catch {
|
|
47
58
|
return null;
|
|
48
59
|
}
|
package/lib/rooms.mjs
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* Gateway subsystem: add-agent-messaging (/orgs/v1/:org_id/rooms/:room_key/*).
|
|
11
11
|
* Session presence cache: ./.run402/messaging.json (gitignore).
|
|
12
12
|
*/
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
13
14
|
import { getSdk } from "./sdk.mjs";
|
|
14
15
|
import { fail, reportSdkError } from "./sdk-errors.mjs";
|
|
15
16
|
import {
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
positionalArgs,
|
|
23
24
|
requirePositionalCount,
|
|
24
25
|
failUnknownSubcommand,
|
|
26
|
+
validateRegularFile,
|
|
25
27
|
} from "./argparse.mjs";
|
|
26
28
|
import {
|
|
27
29
|
resolveRoom,
|
|
@@ -29,19 +31,23 @@ import {
|
|
|
29
31
|
withPresenceRetry,
|
|
30
32
|
registerFreshPresence,
|
|
31
33
|
rememberPresence,
|
|
32
|
-
getRoomState,
|
|
33
34
|
updateRoomState,
|
|
34
35
|
} from "./rooms-context.mjs";
|
|
35
36
|
import { resolveTaskLabel } from "./harness-context.mjs";
|
|
37
|
+
import { ensureFundedWallet } from "./cold-start.mjs";
|
|
36
38
|
|
|
37
39
|
export const IMPORTANCE = ["normal", "high"];
|
|
38
40
|
|
|
39
41
|
const ROOM_FLAGS = ["--project", "--org", "--room"];
|
|
42
|
+
const INVITE_VALUE_FLAGS = [...ROOM_FLAGS, "--note", "--note-file", "--expires-in"];
|
|
40
43
|
|
|
41
44
|
const HELP = `run402 rooms — arrive in a room, see who is live, leave when done
|
|
42
45
|
|
|
43
46
|
Usage:
|
|
44
47
|
run402 rooms join [--name <name>] [--task <text>]
|
|
48
|
+
run402 rooms join <kri1_…> [--json]
|
|
49
|
+
run402 rooms invite [--note <text> | --note-file <path> | stdin]
|
|
50
|
+
[--room <key>] [--expires-in <seconds>] [--json]
|
|
45
51
|
run402 rooms leave [<presence_id>]
|
|
46
52
|
|
|
47
53
|
Addressing:
|
|
@@ -50,6 +56,27 @@ Addressing:
|
|
|
50
56
|
(omit both) Resolved from RUN402_ROOM, a .run402.json binding, or the
|
|
51
57
|
wallet profile's selected org
|
|
52
58
|
|
|
59
|
+
Room Invite (mint a key from the room you stand in, join through one):
|
|
60
|
+
- \`rooms invite\` mints a single-use \`kri1_…\` bearer key. Whoever claims it
|
|
61
|
+
FIRST becomes a permanent \`viewer\` of this org — the narrowest membership
|
|
62
|
+
that can message, and NEVER wider: there is no --role, and a viewer can
|
|
63
|
+
never be auto-admitted as a vault writer. To bring a collaborator into the
|
|
64
|
+
CODE (a vault, a checkpoint, write access), use \`run402 repos invite\`
|
|
65
|
+
instead — this door is talk-only.
|
|
66
|
+
- The key is printed to stdout EXACTLY ONCE (\`--json\` still keeps it out of
|
|
67
|
+
stderr). It is not recoverable if lost — mint a new one.
|
|
68
|
+
- \`rooms join <kri1_…>\` folds a funded-wallet chain (allowance → faucet if
|
|
69
|
+
empty → briefly wait for settlement) and pays a $0.01 testnet seat via
|
|
70
|
+
x402 to claim it — the payment IS the join, so a joiner with no funds
|
|
71
|
+
fails closed rather than joining unpaid. No tier is purchased, no project
|
|
72
|
+
is created. A same-payer replay never pays twice.
|
|
73
|
+
- After a key-form join: the host org becomes this wallet's current org,
|
|
74
|
+
and the binding is written where the next \`run402 messages wait\` reads
|
|
75
|
+
it from — \`.run402.json\` in a plain directory, or local git config
|
|
76
|
+
(\`r402.orgId\`/\`r402.room\`, excluded from git via .git/info/exclude) when
|
|
77
|
+
standing inside a git repository (never a .run402.json committed into a
|
|
78
|
+
stranger's clone).
|
|
79
|
+
|
|
53
80
|
Notes:
|
|
54
81
|
- join registers this session's presence and returns who else is live, what
|
|
55
82
|
they are working on, and what they have claimed — the arrive-and-look call.
|
|
@@ -194,6 +221,261 @@ async function leave(argv) {
|
|
|
194
221
|
}
|
|
195
222
|
}
|
|
196
223
|
|
|
224
|
+
async function readStdinTextLocal() {
|
|
225
|
+
const chunks = [];
|
|
226
|
+
for await (const chunk of process.stdin) {
|
|
227
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
228
|
+
}
|
|
229
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* `rooms invite`'s note is a PLAIN STRING, ≤4 KiB (design D7) — not the
|
|
234
|
+
* structured `kygit.invite-note.v1` JSON `run402 repos invite` reads: the
|
|
235
|
+
* gateway already reads every room message in plaintext, so there is
|
|
236
|
+
* nothing to seal and nothing to schema-validate beyond a length cap.
|
|
237
|
+
* Optional — `undefined` when nothing was supplied and stdin is a TTY.
|
|
238
|
+
*/
|
|
239
|
+
async function readRoomInviteNote(a) {
|
|
240
|
+
const inline = flagValue(a, "--note");
|
|
241
|
+
const noteFile = flagValue(a, "--note-file");
|
|
242
|
+
if (inline != null && noteFile != null) {
|
|
243
|
+
fail({ code: "BAD_USAGE", message: "Pass either --note or --note-file, not both.", hint: "run402 rooms invite --help" });
|
|
244
|
+
}
|
|
245
|
+
if (inline != null) return inline;
|
|
246
|
+
if (noteFile != null) {
|
|
247
|
+
validateRegularFile(noteFile, "--note-file");
|
|
248
|
+
return readFileSync(noteFile, "utf-8");
|
|
249
|
+
}
|
|
250
|
+
if (process.stdin?.isTTY) return undefined; // no note source given — the note is optional
|
|
251
|
+
const text = await readStdinTextLocal();
|
|
252
|
+
return text.trim().length > 0 ? text : undefined;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* `run402 rooms invite` (add-room-invite design D7) — mint from the room
|
|
257
|
+
* this checkout stands in: register-or-resume the inviter's OWN presence
|
|
258
|
+
* FIRST (reusing {@link ensurePresence}, the same logic `rooms join`'s
|
|
259
|
+
* no-key form already runs) so the row carries `inviter_presence_id`, mint,
|
|
260
|
+
* post ONE room fact naming the invite id (never the key), echo the
|
|
261
|
+
* gateway's blast-radius warning to stderr, and print the `kri1_` key to
|
|
262
|
+
* stdout EXACTLY ONCE. A presence or fact failure is reported on the result
|
|
263
|
+
* and never voids the mint.
|
|
264
|
+
*/
|
|
265
|
+
async function invite(argv) {
|
|
266
|
+
const a = normalizeArgv(argv);
|
|
267
|
+
assertKnownFlags(a, [...INVITE_VALUE_FLAGS, "--json", "--help", "-h"], INVITE_VALUE_FLAGS);
|
|
268
|
+
requirePositionalCount(positionalArgs(a, INVITE_VALUE_FLAGS), INVITE_VALUE_FLAGS, {
|
|
269
|
+
min: 0, max: 0, command: "run402 rooms invite",
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const room = await resolveRoom({
|
|
273
|
+
org: flagValue(a, "--org"), room: flagValue(a, "--room"), project: flagValue(a, "--project"),
|
|
274
|
+
});
|
|
275
|
+
const note = await readRoomInviteNote(a);
|
|
276
|
+
const expiresRaw = flagValue(a, "--expires-in");
|
|
277
|
+
const expiresInSeconds = expiresRaw != null ? parseIntegerFlag("--expires-in", expiresRaw, { min: 60, max: 86400 }) : undefined;
|
|
278
|
+
const asJson = a.includes("--json");
|
|
279
|
+
|
|
280
|
+
const sdk = getSdk();
|
|
281
|
+
try {
|
|
282
|
+
const { task } = await resolveTaskLabel({});
|
|
283
|
+
// design D7: register (or resume) the inviter's OWN presence BEFORE
|
|
284
|
+
// minting, so the row carries `inviter_presence_id`. A failure here is
|
|
285
|
+
// reported, never thrown — the mint proceeds without one.
|
|
286
|
+
let inviterPresence = null;
|
|
287
|
+
let inviterPresenceReport = { registered: false };
|
|
288
|
+
try {
|
|
289
|
+
const presence = await ensurePresence(room, { task });
|
|
290
|
+
inviterPresence = presence;
|
|
291
|
+
inviterPresenceReport = { registered: true, presence_id: presence.presence_id, name: presence.name };
|
|
292
|
+
} catch (e) {
|
|
293
|
+
inviterPresenceReport = { registered: false, error: e instanceof Error ? e.message : String(e) };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const result = await sdk.rooms.invite(room.orgId, room.roomKey, {
|
|
297
|
+
...(note !== undefined ? { note } : {}),
|
|
298
|
+
...(inviterPresence ? { inviterPresenceId: inviterPresence.presence_id } : {}),
|
|
299
|
+
...(expiresInSeconds != null ? { expiresInSeconds } : {}),
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
for (const w of result.warnings ?? []) {
|
|
303
|
+
console.error(w.message ?? `${w.code}`);
|
|
304
|
+
}
|
|
305
|
+
console.error(`invite minted: role ${result.role}, expires ${result.expires_at}, room ${result.room?.room_key ?? room.roomKey}`);
|
|
306
|
+
console.error("recipient runs: run402 rooms join <key printed below>");
|
|
307
|
+
|
|
308
|
+
// design D7: post ONE room fact AFTER the mint succeeds — never before
|
|
309
|
+
// (a mint refusal must leave no orphan message), and never naming the
|
|
310
|
+
// key, only the invite id.
|
|
311
|
+
let roomFact = { posted: false, reason: "inviter presence was not registered" };
|
|
312
|
+
if (inviterPresence) {
|
|
313
|
+
const inviteShort = result.invite_id.slice(0, 8);
|
|
314
|
+
try {
|
|
315
|
+
const sent = await sdk.rooms.sendMessage(room.orgId, room.roomKey, {
|
|
316
|
+
body: `Invited another agent to this room (invite ${inviteShort}, expires ${result.expires_at}).`,
|
|
317
|
+
presenceId: inviterPresence.presence_id,
|
|
318
|
+
idempotencyKey: `room-invite:${result.invite_id}:minted`,
|
|
319
|
+
});
|
|
320
|
+
roomFact = { posted: true, message_id: sent.message_id, cursor: sent.cursor };
|
|
321
|
+
// The inviter's own fact must not wake the inviter's next `messages
|
|
322
|
+
// wait` — advance this checkout's stored cursor past it (best-effort).
|
|
323
|
+
try { updateRoomState(room.orgId, room.roomKey, { cursor: sent.cursor }); } catch { /* never fails a mint */ }
|
|
324
|
+
} catch (e) {
|
|
325
|
+
roomFact = { posted: false, reason: e instanceof Error ? e.message : String(e) };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (inviterPresenceReport.registered === false) {
|
|
329
|
+
console.error(`note: your own presence was not registered (${inviterPresenceReport.error}) — the invite still mints and is claimable`);
|
|
330
|
+
}
|
|
331
|
+
if (roomFact.posted === false && inviterPresence) {
|
|
332
|
+
console.error(`note: the room fact was not posted (${roomFact.reason}) — the invite still mints and remains claimable`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const finalResult = { ...result, inviter_presence: inviterPresenceReport, room_fact: roomFact };
|
|
336
|
+
if (asJson) {
|
|
337
|
+
printInviteResultJson(finalResult);
|
|
338
|
+
} else {
|
|
339
|
+
printInviteResultKeyOnly(result);
|
|
340
|
+
}
|
|
341
|
+
} catch (err) {
|
|
342
|
+
reportSdkError(err);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** The key rides the JSON result — still stdout, still exactly once. */
|
|
347
|
+
function printInviteResultJson(finalResult) {
|
|
348
|
+
console.log(JSON.stringify(finalResult, null, 2));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** The key alone, so `KEY=$(run402 rooms invite ...)` works — everything else (the warning, the mint summary) is on stderr. */
|
|
352
|
+
function printInviteResultKeyOnly(result) {
|
|
353
|
+
console.log(result.key);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* `run402 rooms join <kri1_…>` (add-room-invite design D9/D10) — parse the
|
|
358
|
+
* key CLIENT-SIDE first (a wrong-kind vault key refuses by name before ANY
|
|
359
|
+
* network call, including the faucet), fold `ensureFundedWallet` (allowance
|
|
360
|
+
* → faucet-if-empty → brief settlement poll, announced on stderr), claim
|
|
361
|
+
* through the SDK's paid fetch, then leave arrival state exactly where
|
|
362
|
+
* `run402 messages wait` reads it: the host org as this wallet's current
|
|
363
|
+
* org; the binding written to `.run402.json` outside a git repository, or
|
|
364
|
+
* pinned in local git config (and `.run402/` excluded from git) inside one;
|
|
365
|
+
* the returned cursor persisted. There is no `--no-init` — the payment IS
|
|
366
|
+
* the claim.
|
|
367
|
+
*/
|
|
368
|
+
async function joinWithKey(key, a) {
|
|
369
|
+
const asJson = a.includes("--json");
|
|
370
|
+
|
|
371
|
+
// Parse-only pre-check (design D9): refuses a `kgh1_`/`kgi1_` vault key by
|
|
372
|
+
// name, synchronously, before `ensureFundedWallet` ever touches the
|
|
373
|
+
// network — the gateway (faucet included) must never be contacted for a
|
|
374
|
+
// wrong-kind key.
|
|
375
|
+
const { parseRoomInviteKey } = await import("#sdk/node");
|
|
376
|
+
try {
|
|
377
|
+
parseRoomInviteKey(key);
|
|
378
|
+
} catch (err) {
|
|
379
|
+
reportSdkError(err);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
try {
|
|
384
|
+
await ensureFundedWallet((line) => console.error(line));
|
|
385
|
+
const result = await getSdk().rooms.join(key);
|
|
386
|
+
|
|
387
|
+
// Arrival state (design D10) — best-effort throughout: the claim already
|
|
388
|
+
// succeeded, and none of this may fail a completed join.
|
|
389
|
+
try {
|
|
390
|
+
const { setSelectedOrgId } = await import("./org-context.mjs");
|
|
391
|
+
setSelectedOrgId(result.org_id);
|
|
392
|
+
} catch { /* best-effort */ }
|
|
393
|
+
|
|
394
|
+
const cwd = process.cwd();
|
|
395
|
+
let insideGitRepo = false;
|
|
396
|
+
try {
|
|
397
|
+
const { hardenedGit } = await import("#sdk/node");
|
|
398
|
+
await hardenedGit(cwd, ["rev-parse", "--git-dir"]);
|
|
399
|
+
insideGitRepo = true;
|
|
400
|
+
} catch {
|
|
401
|
+
insideGitRepo = false;
|
|
402
|
+
}
|
|
403
|
+
if (!insideGitRepo) {
|
|
404
|
+
try {
|
|
405
|
+
const { updateBindingFile } = await import("./wallet-context.mjs");
|
|
406
|
+
updateBindingFile(cwd, { org: result.org_id, room: result.room.room_key });
|
|
407
|
+
} catch { /* best-effort */ }
|
|
408
|
+
} else {
|
|
409
|
+
try {
|
|
410
|
+
const { pinRoomBinding, excludeMessagingCacheFromGit } = await import("#sdk/node");
|
|
411
|
+
await pinRoomBinding(cwd, { org_id: result.org_id, room_key: result.room.room_key });
|
|
412
|
+
await excludeMessagingCacheFromGit(cwd);
|
|
413
|
+
} catch { /* best-effort */ }
|
|
414
|
+
}
|
|
415
|
+
if (typeof result.cursor === "string") {
|
|
416
|
+
try { updateRoomState(result.org_id, result.room.room_key, { cursor: result.cursor }); } catch { /* best-effort */ }
|
|
417
|
+
}
|
|
418
|
+
// Live-proof defect A: cache the presence the CLAIM ITSELF registered,
|
|
419
|
+
// exactly the way `registerFreshPresence`'s no-key `join` already does
|
|
420
|
+
// via `rememberPresence` — without this, the joiner's very next
|
|
421
|
+
// coordination call (no cached presence_id) registers a SECOND presence
|
|
422
|
+
// for the same arrival. Guarded for an older gateway that predates the
|
|
423
|
+
// `presence` field: `rememberPresence` itself no-ops on anything that
|
|
424
|
+
// isn't `{presence_id: string, ...}`, so this never throws either way.
|
|
425
|
+
try { rememberPresence(result.org_id, result.room.room_key, result.presence); } catch { /* best-effort */ }
|
|
426
|
+
|
|
427
|
+
const nextActions = [...(result.next_actions ?? [])];
|
|
428
|
+
if (!nextActions.some((na) => na.type === "wait_room")) {
|
|
429
|
+
nextActions.push({ type: "wait_room", command: "run402 messages wait", why: "Block until the other agent speaks; silence returns who is still here." });
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (asJson) {
|
|
433
|
+
printClaimResultJson(result, nextActions);
|
|
434
|
+
} else {
|
|
435
|
+
renderClaimResultText(result, nextActions);
|
|
436
|
+
}
|
|
437
|
+
} catch (err) {
|
|
438
|
+
reportSdkError(err);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function printClaimResultJson(result, nextActions) {
|
|
443
|
+
console.log(JSON.stringify({ ...result, next_actions: nextActions }, null, 2));
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function renderClaimResultText(result, nextActions) {
|
|
447
|
+
// The note is plain text, not a structured schema — printed verbatim as
|
|
448
|
+
// Markdown (it may already contain Markdown formatting the inviter wrote).
|
|
449
|
+
if (result.note) {
|
|
450
|
+
console.log(result.note);
|
|
451
|
+
console.error("");
|
|
452
|
+
}
|
|
453
|
+
console.error(`joined org ${result.org_id}, room ${result.room.room_key} — role ${result.membership.role}`);
|
|
454
|
+
if (result.deduplicated) {
|
|
455
|
+
console.error("note: this key was already claimed by this same payer — no second payment was made (safe replay)");
|
|
456
|
+
}
|
|
457
|
+
console.error(`seat: $${(result.seat.amount_usd_micros / 1_000_000).toFixed(2)} on ${result.seat.network}${result.seat.charge_id ? ` (charge ${result.seat.charge_id})` : ""}`);
|
|
458
|
+
if (result.inviter) {
|
|
459
|
+
const labels = [result.inviter.program, result.inviter.model].filter(Boolean).join("/");
|
|
460
|
+
const liveness = result.inviter.state === "active" ? "live" : result.inviter.state;
|
|
461
|
+
console.error(`invited by ${result.inviter.name}${labels ? ` (${labels})` : ""} — ${liveness}`);
|
|
462
|
+
} else {
|
|
463
|
+
console.error("invited by: unknown (the inviter never registered a presence)");
|
|
464
|
+
}
|
|
465
|
+
const others = (result.live_presences ?? []);
|
|
466
|
+
if (others.length > 0) console.error(`also live: ${others.map((p) => p.name).join(", ")}`);
|
|
467
|
+
const recent = result.recent_messages ?? [];
|
|
468
|
+
if (recent.length > 0) {
|
|
469
|
+
console.error(`recent messages (${recent.length}):`);
|
|
470
|
+
for (const m of recent.slice().reverse()) {
|
|
471
|
+
console.error(` ${m.sender ?? "?"}: ${m.body_snippet ?? m.body ?? ""}`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
for (const na of nextActions) {
|
|
475
|
+
if (na.command) console.error(`next: ${na.command}${na.why ? ` — ${na.why}` : ""}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
197
479
|
export async function run(sub, args) {
|
|
198
480
|
const argv = Array.isArray(args) ? args : [];
|
|
199
481
|
if (!sub || hasHelp([sub, ...argv])) {
|
|
@@ -202,7 +484,22 @@ export async function run(sub, args) {
|
|
|
202
484
|
}
|
|
203
485
|
switch (sub) {
|
|
204
486
|
case "join": {
|
|
205
|
-
|
|
487
|
+
// `run402 rooms join <kri1_…>` (add-room-invite design D9): a
|
|
488
|
+
// positional key form claims a seat and arrives; no positional keeps
|
|
489
|
+
// the existing arrive-and-look behavior unchanged.
|
|
490
|
+
const a = normalizeArgv(argv);
|
|
491
|
+
const positionals = positionalArgs(a, ["--name", "--task", ...ROOM_FLAGS]);
|
|
492
|
+
if (positionals.length === 0) {
|
|
493
|
+
await who(argv);
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
assertKnownFlags(a, ["--json", "--help", "-h"], []);
|
|
497
|
+
requirePositionalCount(positionals, [], { min: 1, max: 1, command: "run402 rooms join [<kri1_…>]" });
|
|
498
|
+
await joinWithKey(positionals[0], a);
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
case "invite": {
|
|
502
|
+
await invite(argv);
|
|
206
503
|
break;
|
|
207
504
|
}
|
|
208
505
|
case "leave": {
|
package/lib/sdk-errors.mjs
CHANGED
|
@@ -63,6 +63,20 @@ export function parseFlagJson(name, value) {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// Live-proof defect B — the gateway's own terminal room-invite claim
|
|
67
|
+
// refusals (never settled; see `sdk/src/namespaces/rooms.ts`'s
|
|
68
|
+
// `ROOM_INVITE_TERMINAL_REFUSAL_CODES`, the source of truth this mirrors).
|
|
69
|
+
// A small, deliberate duplication rather than an import, so this
|
|
70
|
+
// dependency-free error reporter stays that way — these five strings are
|
|
71
|
+
// gateway-owned and stable.
|
|
72
|
+
const ROOM_INVITE_TERMINAL_REFUSAL_CODES = new Set([
|
|
73
|
+
"ROOM_INVITE_KEY_INVALID",
|
|
74
|
+
"ROOM_INVITE_KEY_EXPIRED",
|
|
75
|
+
"ROOM_INVITE_KEY_REVOKED",
|
|
76
|
+
"ROOM_INVITE_KEY_ALREADY_CLAIMED",
|
|
77
|
+
"ROOM_INVITE_CLAIM_REQUIRES_WALLET",
|
|
78
|
+
]);
|
|
79
|
+
|
|
66
80
|
export function reportSdkError(err) {
|
|
67
81
|
if (err?.name === "ProjectCredentialNotFound" || err?.code === "PROJECT_CREDENTIAL_NOT_FOUND") {
|
|
68
82
|
fail({
|
|
@@ -137,6 +151,17 @@ export function reportSdkError(err) {
|
|
|
137
151
|
}
|
|
138
152
|
}
|
|
139
153
|
|
|
154
|
+
// Live-proof defect B: a terminal room-invite claim refusal is the
|
|
155
|
+
// gateway's OWN typed envelope by the time it reaches here (the SDK's
|
|
156
|
+
// default paid fetch classifies these five codes as `"failed"`, never
|
|
157
|
+
// `"ambiguous"` — see `sdk/src/node/paid-fetch.ts` and
|
|
158
|
+
// `isTerminalRoomInviteRefusal`), so the truthful, reassuring fact —
|
|
159
|
+
// no payment was charged — is worth stating explicitly rather than
|
|
160
|
+
// leaving the caller to infer it from `mutation_state`.
|
|
161
|
+
if (ROOM_INVITE_TERMINAL_REFUSAL_CODES.has(payload.code) && payload.hint === undefined) {
|
|
162
|
+
payload.hint = "This key was not claimable (already used, expired, revoked, or this route requires a wallet, not a session) — you were not charged; no payment was made for this attempt.";
|
|
163
|
+
}
|
|
164
|
+
|
|
140
165
|
// Keep `status: "error"` as the outer envelope even if the response body
|
|
141
166
|
// happened to contain its own `status` field (e.g. `{"status":"degraded"}`
|
|
142
167
|
// from /health 503 responses). Downstream scripts match on this sentinel.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "run402",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.71.0",
|
|
4
4
|
"description": "CLI for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* keep coordinating.
|
|
30
30
|
*/
|
|
31
31
|
import type { Client } from "../kernel.js";
|
|
32
|
-
import type { AckRoomMessageOptions, CreatedRoomClaim, CreateRoomClaimInput, ListPresencesOptions, ListRoomClaimsOptions, ListRoomMessagesOptions, PresenceRegistration, RegisterPresenceOptions, RoomAckResult, RoomClaimList, RoomClaimReleaseResult, RoomMessage, RoomLeaveResult, RoomList, RoomMessagePage, RoomMessageWaitResult, RoomPresence, RoomPresenceList, RoomSummary, SendRoomMessageInput, SentRoomMessage, WaitForRoomMessagesOptions } from "./rooms.types.js";
|
|
32
|
+
import type { AckRoomMessageOptions, CreatedRoomClaim, CreateRoomClaimInput, ListPresencesOptions, ListRoomClaimsOptions, ListRoomMessagesOptions, PresenceRegistration, RegisterPresenceOptions, RoomAckResult, RoomClaimList, RoomClaimReleaseResult, RoomInviteJoinResult, RoomInviteMintOptions, RoomInviteMintResult, RoomMessage, RoomLeaveResult, RoomList, RoomMessagePage, RoomMessageWaitResult, RoomPresence, RoomPresenceList, RoomSummary, SendRoomMessageInput, SentRoomMessage, WaitForRoomMessagesOptions } from "./rooms.types.js";
|
|
33
33
|
export declare class Rooms {
|
|
34
34
|
private readonly client;
|
|
35
35
|
constructor(client: Client);
|
|
@@ -172,6 +172,36 @@ export declare class Rooms {
|
|
|
172
172
|
* `already_released: true` with the original time.
|
|
173
173
|
*/
|
|
174
174
|
releaseClaim(orgId: string, roomKey: string, claimId: string): Promise<RoomClaimReleaseResult>;
|
|
175
|
+
/**
|
|
176
|
+
* Mint a Room Invite Key from the room the caller stands in
|
|
177
|
+
* (`POST /orgs/v1/:org_id/rooms/:room_key/invites`) — a single-use bearer
|
|
178
|
+
* key (`kri1_…`) whose claimant becomes a permanent `viewer` of the org,
|
|
179
|
+
* the narrowest membership that can message (design D4: never `--role`,
|
|
180
|
+
* never wider, never auto-admitted as a vault writer). Requires
|
|
181
|
+
* `developer`+ (session, wallet, or admin credential — a delegate is
|
|
182
|
+
* refused, since a room invite confers org membership).
|
|
183
|
+
*
|
|
184
|
+
* `invite_id` and `master_secret` are generated LOCALLY (design D3): the
|
|
185
|
+
* gateway never sees `master_secret`, only the SHA-256 `auth_hash` this
|
|
186
|
+
* call derives and sends. The assembled key is returned exactly ONCE —
|
|
187
|
+
* nothing here or downstream persists it.
|
|
188
|
+
*/
|
|
189
|
+
invite(orgId: string, roomKey: string, opts?: RoomInviteMintOptions): Promise<RoomInviteMintResult>;
|
|
190
|
+
/**
|
|
191
|
+
* Claim a Room Invite Key (`POST /rooms/v1/invites/:invite_id/claim`) —
|
|
192
|
+
* parses the key CLIENT-SIDE first, refusing a `kgh1_`/`kgi1_` vault key
|
|
193
|
+
* BY NAME (pointing at `run402 repos resume`/`run402 repos join`) before
|
|
194
|
+
* any network call (design D3). The claim is an x402-PAID resource (the
|
|
195
|
+
* `room_seat` SKU, testnet only): the VERIFIED PAYER of that payment
|
|
196
|
+
* becomes the claimant, so this call is sent through the client's paid
|
|
197
|
+
* fetch WITHOUT a bearer credential (`withAuth: false`) — no
|
|
198
|
+
* `SIGN-IN-WITH-X` header, and any cached control-plane session is
|
|
199
|
+
* deliberately not attached, exactly matching the gateway's own
|
|
200
|
+
* `403 ROOM_INVITE_CLAIM_REQUIRES_WALLET` refusal for a bearer-credentialed
|
|
201
|
+
* request at this route. A same-payer replay never pays twice
|
|
202
|
+
* (`deduplicated: true`, no second charge).
|
|
203
|
+
*/
|
|
204
|
+
join(key: string): Promise<RoomInviteJoinResult>;
|
|
175
205
|
/**
|
|
176
206
|
* Return a room-scoped sub-client with `(orgId, roomKey)` pre-bound.
|
|
177
207
|
* Synchronous — both ids are explicit. For a project's default room
|
|
@@ -187,6 +217,26 @@ export declare class Rooms {
|
|
|
187
217
|
*/
|
|
188
218
|
forProject(projectId: string): Promise<ScopedRoom>;
|
|
189
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* Terminal room-invite claim refusals the gateway's own x402 paywall NEVER
|
|
222
|
+
* settles for (design D5 — the paywall buffers and settles only on a
|
|
223
|
+
* sub-400 response, so any of these five codes means no payment ever
|
|
224
|
+
* completed, refunded or otherwise). Live-proof defect B: without this, a
|
|
225
|
+
* spent/expired/revoked key, or a bearer credential presented to the claim
|
|
226
|
+
* route, surfaced as a generic `X402_PAYMENT_OUTCOME_AMBIGUOUS` — alarming
|
|
227
|
+
* and wrong, since the gateway had already answered with one of these and
|
|
228
|
+
* moved no funds. `node/paid-fetch.ts`'s default paid fetch recognizes a
|
|
229
|
+
* response carrying one of these codes, FROM this SDK's own configured API
|
|
230
|
+
* origin, as `"failed"` rather than `"ambiguous"` — letting the gateway's
|
|
231
|
+
* own typed envelope (not a synthesized payment-attempt error) reach the
|
|
232
|
+
* caller. Scoped by CODE, never by route: an arbitrary third-party paid URL
|
|
233
|
+
* — even one that echoes one of these exact strings — is never treated as
|
|
234
|
+
* this SDK's own origin, so its non-2xx after dispatch stays genuinely
|
|
235
|
+
* ambiguous, unchanged.
|
|
236
|
+
*/
|
|
237
|
+
export declare const ROOM_INVITE_TERMINAL_REFUSAL_CODES: ReadonlySet<string>;
|
|
238
|
+
/** True when `envelope.code` is one of {@link ROOM_INVITE_TERMINAL_REFUSAL_CODES}. */
|
|
239
|
+
export declare function isTerminalRoomInviteRefusal(envelope: Record<string, unknown> | null | undefined): boolean;
|
|
190
240
|
/**
|
|
191
241
|
* A room-scoped sub-client returned by {@link Rooms.scoped} /
|
|
192
242
|
* {@link Rooms.forProject}. The `(orgId, roomKey)` pair is bound at
|
|
@@ -225,5 +275,7 @@ export declare class ScopedRoom {
|
|
|
225
275
|
listClaims(opts?: ListRoomClaimsOptions): Promise<RoomClaimList>;
|
|
226
276
|
/** See {@link Rooms.releaseClaim}. */
|
|
227
277
|
releaseClaim(claimId: string): Promise<RoomClaimReleaseResult>;
|
|
278
|
+
/** See {@link Rooms.invite} — pre-bound to this room. */
|
|
279
|
+
invite(opts?: RoomInviteMintOptions): Promise<RoomInviteMintResult>;
|
|
228
280
|
}
|
|
229
281
|
//# sourceMappingURL=rooms.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rooms.d.ts","sourceRoot":"","sources":["../../src/namespaces/rooms.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"rooms.d.ts","sourceRoot":"","sources":["../../src/namespaces/rooms.ts"],"names":[],"mappings":"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"}
|