@fulldivevr/workcord-cli 1.0.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/LICENSE +18 -0
- package/README.md +312 -0
- package/dist/auth.js +9 -0
- package/dist/cli.js +46 -0
- package/dist/client.js +125 -0
- package/dist/commands/_group.js +5 -0
- package/dist/commands/auth.js +125 -0
- package/dist/commands/challenges.js +51 -0
- package/dist/commands/custom-agents.js +120 -0
- package/dist/commands/dm.js +35 -0
- package/dist/commands/messages.js +360 -0
- package/dist/commands/room-agents.js +47 -0
- package/dist/commands/rooms.js +687 -0
- package/dist/commands/users.js +126 -0
- package/dist/commands/whoami.js +15 -0
- package/dist/generated/_helpers.js +8 -0
- package/dist/generated/commands/activity.js +46 -0
- package/dist/generated/commands/ai.js +40 -0
- package/dist/generated/commands/ambassador.js +34 -0
- package/dist/generated/commands/apple-iap.js +61 -0
- package/dist/generated/commands/attribution.js +130 -0
- package/dist/generated/commands/auth.js +205 -0
- package/dist/generated/commands/challenges.js +251 -0
- package/dist/generated/commands/clips.js +71 -0
- package/dist/generated/commands/contact-match.js +92 -0
- package/dist/generated/commands/content-crawler.js +27 -0
- package/dist/generated/commands/custom-agents.js +346 -0
- package/dist/generated/commands/desktop-releases.js +236 -0
- package/dist/generated/commands/discover.js +236 -0
- package/dist/generated/commands/dm.js +155 -0
- package/dist/generated/commands/drafts.js +41 -0
- package/dist/generated/commands/embed.js +34 -0
- package/dist/generated/commands/explore.js +19 -0
- package/dist/generated/commands/feed.js +67 -0
- package/dist/generated/commands/feedback.js +44 -0
- package/dist/generated/commands/gamification.js +33 -0
- package/dist/generated/commands/google-play-iap.js +73 -0
- package/dist/generated/commands/health.js +32 -0
- package/dist/generated/commands/internal.js +162 -0
- package/dist/generated/commands/interview.js +76 -0
- package/dist/generated/commands/invite-redirect.js +20 -0
- package/dist/generated/commands/jesuscord-admin.js +36 -0
- package/dist/generated/commands/join.js +27 -0
- package/dist/generated/commands/kits.js +108 -0
- package/dist/generated/commands/meetings.js +81 -0
- package/dist/generated/commands/message-folders.js +68 -0
- package/dist/generated/commands/messages.js +418 -0
- package/dist/generated/commands/one-emulator.js +32 -0
- package/dist/generated/commands/pdf-export.js +19 -0
- package/dist/generated/commands/pin-folders.js +72 -0
- package/dist/generated/commands/pinned-messages.js +81 -0
- package/dist/generated/commands/polls.js +152 -0
- package/dist/generated/commands/premium-feeds.js +32 -0
- package/dist/generated/commands/processors.js +22 -0
- package/dist/generated/commands/questions.js +54 -0
- package/dist/generated/commands/referrals.js +19 -0
- package/dist/generated/commands/reports.js +21 -0
- package/dist/generated/commands/room-a2-a.js +81 -0
- package/dist/generated/commands/room-agents.js +273 -0
- package/dist/generated/commands/room-invites.js +155 -0
- package/dist/generated/commands/room-owned-agent.js +36 -0
- package/dist/generated/commands/room-page.js +20 -0
- package/dist/generated/commands/rooms.js +1117 -0
- package/dist/generated/commands/saved-messages.js +84 -0
- package/dist/generated/commands/saved-stories.js +125 -0
- package/dist/generated/commands/search.js +44 -0
- package/dist/generated/commands/shadow-home-agent.js +36 -0
- package/dist/generated/commands/shadow.js +64 -0
- package/dist/generated/commands/share-cards.js +85 -0
- package/dist/generated/commands/share.js +20 -0
- package/dist/generated/commands/sponsors.js +20 -0
- package/dist/generated/commands/stories.js +324 -0
- package/dist/generated/commands/story-by-id.js +20 -0
- package/dist/generated/commands/submissions.js +112 -0
- package/dist/generated/commands/subscriptions.js +103 -0
- package/dist/generated/commands/tips.js +76 -0
- package/dist/generated/commands/users.js +373 -0
- package/dist/generated/commands/voice-token.js +21 -0
- package/dist/generated/commands/well-known.js +32 -0
- package/dist/generated/commands/wrapped.js +20 -0
- package/dist/generated/index.js +131 -0
- package/package.json +60 -0
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { basename, extname } from "node:path";
|
|
3
|
+
import { callMultipart } from "../client.js";
|
|
4
|
+
import { call } from "../client.js";
|
|
5
|
+
import { getOrCreateGroup } from "./_group.js";
|
|
6
|
+
const IMAGE_MIME = {
|
|
7
|
+
".png": "image/png",
|
|
8
|
+
".jpg": "image/jpeg",
|
|
9
|
+
".jpeg": "image/jpeg",
|
|
10
|
+
".gif": "image/gif",
|
|
11
|
+
".webp": "image/webp",
|
|
12
|
+
};
|
|
13
|
+
const DEFAULT_ROOM_LIMIT = 50;
|
|
14
|
+
const MAX_ROOM_LIMIT = 100;
|
|
15
|
+
function currentUser(program) {
|
|
16
|
+
const { as } = program.opts();
|
|
17
|
+
if (!as)
|
|
18
|
+
throw new Error("--as <userId> is required");
|
|
19
|
+
return as;
|
|
20
|
+
}
|
|
21
|
+
function parseLimit(v) {
|
|
22
|
+
const limit = Number(v);
|
|
23
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > MAX_ROOM_LIMIT) {
|
|
24
|
+
throw new Error(`--limit must be an integer from 1 to ${MAX_ROOM_LIMIT}`);
|
|
25
|
+
}
|
|
26
|
+
return limit;
|
|
27
|
+
}
|
|
28
|
+
function parsePage(v) {
|
|
29
|
+
if (v === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
const page = Number(v);
|
|
32
|
+
if (!Number.isInteger(page) || page < 1) {
|
|
33
|
+
throw new Error("--page must be a positive integer");
|
|
34
|
+
}
|
|
35
|
+
return page;
|
|
36
|
+
}
|
|
37
|
+
function list(v) {
|
|
38
|
+
return v?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
39
|
+
}
|
|
40
|
+
function bool(v, name) {
|
|
41
|
+
if (v === undefined)
|
|
42
|
+
return undefined;
|
|
43
|
+
if (v === "true")
|
|
44
|
+
return true;
|
|
45
|
+
if (v === "false")
|
|
46
|
+
return false;
|
|
47
|
+
throw new Error(`${name} must be true or false`);
|
|
48
|
+
}
|
|
49
|
+
function json(v, name) {
|
|
50
|
+
if (v === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(v);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new Error(`${name} must be valid JSON`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function normalizeInviteToken(token) {
|
|
60
|
+
const trimmed = token.trim();
|
|
61
|
+
if (!trimmed)
|
|
62
|
+
throw new Error("--invite-token must not be empty");
|
|
63
|
+
return trimmed.startsWith("i-") ? trimmed : `i-${trimmed}`;
|
|
64
|
+
}
|
|
65
|
+
function roomRecord(room) {
|
|
66
|
+
return room && typeof room === "object" ? room : {};
|
|
67
|
+
}
|
|
68
|
+
function normalizedRoom(room) {
|
|
69
|
+
const r = roomRecord(room);
|
|
70
|
+
const id = r.id ?? r.roomId ?? null;
|
|
71
|
+
const accessLevel = r.accessLevel ?? null;
|
|
72
|
+
return {
|
|
73
|
+
id,
|
|
74
|
+
roomId: id,
|
|
75
|
+
name: r.name ?? r.roomName ?? null,
|
|
76
|
+
// GET /discover/rooms/enriched strips `description` from every item, so
|
|
77
|
+
// discover/trending output is always null here — fetch the room directly for it.
|
|
78
|
+
description: r.description ?? null,
|
|
79
|
+
tags: Array.isArray(r.tags) ? r.tags : [],
|
|
80
|
+
memberCount: r.memberCount ?? null,
|
|
81
|
+
isPublic: r.isPublic ?? accessLevel === "open",
|
|
82
|
+
joinCode: r.joinCode ?? null,
|
|
83
|
+
requiresApproval: accessLevel === "approvalGate",
|
|
84
|
+
accessLevel,
|
|
85
|
+
createdAt: r.createdAt ?? null,
|
|
86
|
+
updatedAt: r.updatedAt ?? null,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function normalizedRoomList(payload) {
|
|
90
|
+
const p = roomRecord(payload);
|
|
91
|
+
const data = Array.isArray(p.data)
|
|
92
|
+
? p.data
|
|
93
|
+
: Array.isArray(p.rooms)
|
|
94
|
+
? p.rooms
|
|
95
|
+
: [];
|
|
96
|
+
const cursor = p.cursor ?? null;
|
|
97
|
+
return {
|
|
98
|
+
data: data.map(normalizedRoom),
|
|
99
|
+
count: data.length,
|
|
100
|
+
cursor,
|
|
101
|
+
nextCursor: cursor,
|
|
102
|
+
hasMore: Boolean(p.hasMore ?? cursor),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function inviteResolution(inviteToken, room) {
|
|
106
|
+
const r = roomRecord(room);
|
|
107
|
+
if (r.type === "app_invite") {
|
|
108
|
+
return {
|
|
109
|
+
type: "app_invite",
|
|
110
|
+
inviteToken,
|
|
111
|
+
roomId: null,
|
|
112
|
+
roomName: null,
|
|
113
|
+
roomPurpose: null,
|
|
114
|
+
inviterUserId: null,
|
|
115
|
+
isJoinable: false,
|
|
116
|
+
requiresApproval: false,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const normalized = normalizedRoom(room);
|
|
120
|
+
return {
|
|
121
|
+
type: "room_invite",
|
|
122
|
+
inviteToken,
|
|
123
|
+
roomId: normalized.roomId,
|
|
124
|
+
roomName: normalized.name,
|
|
125
|
+
roomPurpose: normalized.description,
|
|
126
|
+
inviterUserId: null,
|
|
127
|
+
isJoinable: normalized.roomId !== null,
|
|
128
|
+
requiresApproval: normalized.requiresApproval,
|
|
129
|
+
room: normalized,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function applyRoomVisibility(body, opts) {
|
|
133
|
+
if (opts.public && opts.private)
|
|
134
|
+
throw new Error("Use either --public or --private, not both");
|
|
135
|
+
if (opts.public)
|
|
136
|
+
body.isPublic = true;
|
|
137
|
+
if (opts.private)
|
|
138
|
+
body.isPublic = false;
|
|
139
|
+
}
|
|
140
|
+
function addRoomShapeOptions(command, opts = {}) {
|
|
141
|
+
let withOptions = command;
|
|
142
|
+
if (opts.createOnly) {
|
|
143
|
+
withOptions = withOptions.option("--type <value>", "room type");
|
|
144
|
+
}
|
|
145
|
+
return withOptions
|
|
146
|
+
.option("--public", "make room public")
|
|
147
|
+
.option("--private", "make room private")
|
|
148
|
+
.option("--category <value>", "room category")
|
|
149
|
+
.option("--tags <items>", "comma-separated tags")
|
|
150
|
+
.option("--access-level <value>", "access level")
|
|
151
|
+
.option("--member-identity <value>", "member identity policy")
|
|
152
|
+
.option("--agents <items>", "comma-separated agent ids")
|
|
153
|
+
.option("--content-formats <items>", "comma-separated content formats")
|
|
154
|
+
.option("--lens <value>", "room lens")
|
|
155
|
+
.option("--inbox-category <value>", "inbox category")
|
|
156
|
+
.option("--in-room-agents <items>", "comma-separated in-room agent ids")
|
|
157
|
+
.option("--pin-permission <value>", "pin permission")
|
|
158
|
+
.option("--only-admins-can-post <true|false>", "whether only admins can post")
|
|
159
|
+
.option("--web-app-url <url>", "web app URL")
|
|
160
|
+
.option("--briefing-time <value>", "briefing time")
|
|
161
|
+
.option("--briefing-languages <items>", "comma-separated briefing languages")
|
|
162
|
+
.option("--sources <json>", "sources array as JSON")
|
|
163
|
+
.option("--content-filters <json>", "content filters object as JSON");
|
|
164
|
+
}
|
|
165
|
+
function roomBody(opts, mode) {
|
|
166
|
+
const body = {};
|
|
167
|
+
applyRoomVisibility(body, opts);
|
|
168
|
+
if (opts.name !== undefined)
|
|
169
|
+
body.name = opts.name;
|
|
170
|
+
if (mode === "create" && "type" in opts && opts.type !== undefined)
|
|
171
|
+
body.type = opts.type;
|
|
172
|
+
if (mode === "create" && "language" in opts && opts.language !== undefined)
|
|
173
|
+
body.language = opts.language;
|
|
174
|
+
if (mode === "create" && "installAgentId" in opts && opts.installAgentId !== undefined)
|
|
175
|
+
body.installAgentId = opts.installAgentId;
|
|
176
|
+
if (opts.category !== undefined)
|
|
177
|
+
body.category = opts.category;
|
|
178
|
+
if (opts.accessLevel !== undefined)
|
|
179
|
+
body.accessLevel = opts.accessLevel;
|
|
180
|
+
if (opts.memberIdentity !== undefined)
|
|
181
|
+
body.memberIdentity = opts.memberIdentity;
|
|
182
|
+
if (opts.lens !== undefined)
|
|
183
|
+
body.lens = opts.lens;
|
|
184
|
+
if (opts.inboxCategory !== undefined)
|
|
185
|
+
body.inboxCategory = opts.inboxCategory;
|
|
186
|
+
if (opts.pinPermission !== undefined)
|
|
187
|
+
body.pinPermission = opts.pinPermission;
|
|
188
|
+
if (opts.webAppUrl !== undefined)
|
|
189
|
+
body.webAppUrl = opts.webAppUrl;
|
|
190
|
+
if (opts.briefingTime !== undefined)
|
|
191
|
+
body.briefingTime = opts.briefingTime;
|
|
192
|
+
if ("iconUrl" in opts && opts.iconUrl !== undefined)
|
|
193
|
+
body.iconUrl = opts.iconUrl;
|
|
194
|
+
const tags = list(opts.tags);
|
|
195
|
+
if (tags !== undefined)
|
|
196
|
+
body.tags = tags;
|
|
197
|
+
const agents = list(opts.agents);
|
|
198
|
+
if (agents !== undefined)
|
|
199
|
+
body.agents = agents;
|
|
200
|
+
const contentFormats = list(opts.contentFormats);
|
|
201
|
+
if (contentFormats !== undefined)
|
|
202
|
+
body.contentFormats = contentFormats;
|
|
203
|
+
const inRoomAgents = list(opts.inRoomAgents);
|
|
204
|
+
if (inRoomAgents !== undefined)
|
|
205
|
+
body.inRoomAgents = inRoomAgents;
|
|
206
|
+
const briefingLanguages = list(opts.briefingLanguages);
|
|
207
|
+
if (briefingLanguages !== undefined)
|
|
208
|
+
body.briefingLanguages = briefingLanguages;
|
|
209
|
+
const onlyAdminsCanPost = bool(opts.onlyAdminsCanPost, "--only-admins-can-post");
|
|
210
|
+
if (onlyAdminsCanPost !== undefined)
|
|
211
|
+
body.onlyAdminsCanPost = onlyAdminsCanPost;
|
|
212
|
+
const sources = json(opts.sources, "--sources");
|
|
213
|
+
if (sources !== undefined)
|
|
214
|
+
body.sources = sources;
|
|
215
|
+
const contentFilters = json(opts.contentFilters, "--content-filters");
|
|
216
|
+
if (contentFilters !== undefined)
|
|
217
|
+
body.contentFilters = contentFilters;
|
|
218
|
+
return body;
|
|
219
|
+
}
|
|
220
|
+
export function registerRooms(program) {
|
|
221
|
+
const group = getOrCreateGroup(program, "rooms", "Rooms operations");
|
|
222
|
+
group
|
|
223
|
+
.command("list")
|
|
224
|
+
.description("List the acting user's rooms. Use --all to follow pagination and return one combined result.")
|
|
225
|
+
.option("--all", "fetch every page")
|
|
226
|
+
.option("--cursor <value>", "pagination cursor")
|
|
227
|
+
.option("--limit <n>", `page size, 1-${MAX_ROOM_LIMIT}`, String(DEFAULT_ROOM_LIMIT))
|
|
228
|
+
.option("--status <value>", "room status filter")
|
|
229
|
+
.option("--inbox-category <value>", "inbox category filter")
|
|
230
|
+
.option("--no-shadow-guidance", "do not opt in to Shadow guidance cards")
|
|
231
|
+
.action(async (opts) => {
|
|
232
|
+
const asUserId = currentUser(program);
|
|
233
|
+
const limit = parseLimit(opts.limit);
|
|
234
|
+
if (opts.all && opts.cursor)
|
|
235
|
+
throw new Error("Use either --all or --cursor, not both");
|
|
236
|
+
const query = {
|
|
237
|
+
inboxCategory: opts.inboxCategory,
|
|
238
|
+
status: opts.status,
|
|
239
|
+
limit,
|
|
240
|
+
cursor: opts.cursor,
|
|
241
|
+
shadowGuidanceCards: opts.shadowGuidance ? "true" : undefined,
|
|
242
|
+
};
|
|
243
|
+
if (!opts.all) {
|
|
244
|
+
const data = await call({ method: "GET", path: "/api/v1/rooms", query, asUserId });
|
|
245
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const rooms = [];
|
|
249
|
+
let cursor;
|
|
250
|
+
let hasMore = true;
|
|
251
|
+
while (hasMore) {
|
|
252
|
+
const page = (await call({
|
|
253
|
+
method: "GET",
|
|
254
|
+
path: "/api/v1/rooms",
|
|
255
|
+
query: { ...query, cursor },
|
|
256
|
+
asUserId,
|
|
257
|
+
}));
|
|
258
|
+
rooms.push(...(page.data ?? []));
|
|
259
|
+
cursor = page.cursor ?? undefined;
|
|
260
|
+
hasMore = Boolean(page.hasMore && cursor);
|
|
261
|
+
}
|
|
262
|
+
process.stdout.write(JSON.stringify({ data: rooms, count: rooms.length, cursor: null, hasMore: false }) + "\n");
|
|
263
|
+
});
|
|
264
|
+
group
|
|
265
|
+
.command("list-app")
|
|
266
|
+
.description("List discoverable/open app rooms. Use --all to follow pagination and return one combined result.")
|
|
267
|
+
.option("--all", "fetch every page")
|
|
268
|
+
.option("--category <value>", "category filter")
|
|
269
|
+
.option("--cursor <value>", "pagination cursor")
|
|
270
|
+
.option("--language <value>", "language filter")
|
|
271
|
+
.option("--limit <n>", `page size, 1-${MAX_ROOM_LIMIT}`, String(DEFAULT_ROOM_LIMIT))
|
|
272
|
+
.option("--page <n>", "page number for page-based sorts")
|
|
273
|
+
.option("--search <query>", "room name search")
|
|
274
|
+
.option("--sort <value>", "sort: active, new, best, trending")
|
|
275
|
+
.option("--tags <items>", "comma-separated tag filters")
|
|
276
|
+
.action(async (opts) => {
|
|
277
|
+
const asUserId = currentUser(program);
|
|
278
|
+
const limit = parseLimit(opts.limit);
|
|
279
|
+
const page = parsePage(opts.page);
|
|
280
|
+
if (opts.all && (opts.cursor || page !== undefined)) {
|
|
281
|
+
throw new Error("Use either --all or --cursor/--page, not both");
|
|
282
|
+
}
|
|
283
|
+
if (opts.cursor && page !== undefined) {
|
|
284
|
+
throw new Error("Use either --cursor or --page, not both");
|
|
285
|
+
}
|
|
286
|
+
const query = {
|
|
287
|
+
category: opts.category,
|
|
288
|
+
cursor: opts.cursor,
|
|
289
|
+
language: opts.language,
|
|
290
|
+
limit,
|
|
291
|
+
page,
|
|
292
|
+
search: opts.search,
|
|
293
|
+
sort: opts.sort,
|
|
294
|
+
tags: opts.tags,
|
|
295
|
+
};
|
|
296
|
+
if (!opts.all) {
|
|
297
|
+
const data = (await call({
|
|
298
|
+
method: "GET",
|
|
299
|
+
path: "/api/v1/discover/rooms/enriched",
|
|
300
|
+
query,
|
|
301
|
+
asUserId,
|
|
302
|
+
}));
|
|
303
|
+
process.stdout.write(JSON.stringify({ ...data, count: data.data?.length ?? 0, nextCursor: data.cursor ?? null }) + "\n");
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const rooms = [];
|
|
307
|
+
const pageSort = opts.sort === "best" || opts.sort === "trending";
|
|
308
|
+
let cursor;
|
|
309
|
+
let currentPage = 1;
|
|
310
|
+
let hasMore = true;
|
|
311
|
+
while (hasMore) {
|
|
312
|
+
const pageQuery = pageSort
|
|
313
|
+
? { ...query, cursor: undefined, page: currentPage }
|
|
314
|
+
: { ...query, cursor, page: undefined };
|
|
315
|
+
const pageData = (await call({
|
|
316
|
+
method: "GET",
|
|
317
|
+
path: "/api/v1/discover/rooms/enriched",
|
|
318
|
+
query: pageQuery,
|
|
319
|
+
asUserId,
|
|
320
|
+
}));
|
|
321
|
+
rooms.push(...(pageData.data ?? []));
|
|
322
|
+
hasMore = Boolean(pageData.hasMore);
|
|
323
|
+
if (pageSort) {
|
|
324
|
+
currentPage += 1;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
cursor = pageData.cursor ?? undefined;
|
|
328
|
+
hasMore = Boolean(hasMore && cursor);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
process.stdout.write(JSON.stringify({ data: rooms, count: rooms.length, cursor: null, nextCursor: null, hasMore: false }) + "\n");
|
|
332
|
+
});
|
|
333
|
+
group
|
|
334
|
+
.command("discover")
|
|
335
|
+
.description("Discover open rooms with normalized JSON output.")
|
|
336
|
+
.option("--category <value>", "category filter")
|
|
337
|
+
.option("--cursor <value>", "pagination cursor")
|
|
338
|
+
.option("--language <value>", "language filter")
|
|
339
|
+
.option("--limit <n>", `page size, 1-${MAX_ROOM_LIMIT}`, String(DEFAULT_ROOM_LIMIT))
|
|
340
|
+
.option("--page <n>", "page number for page-based sorts")
|
|
341
|
+
.option("--search <query>", "room name search")
|
|
342
|
+
.option("--sort <value>", "sort: active, new, best, trending")
|
|
343
|
+
.option("--tags <items>", "comma-separated tag filters")
|
|
344
|
+
.action(async (opts) => {
|
|
345
|
+
const data = await call({
|
|
346
|
+
method: "GET",
|
|
347
|
+
path: "/api/v1/discover/rooms/enriched",
|
|
348
|
+
query: {
|
|
349
|
+
category: opts.category,
|
|
350
|
+
cursor: opts.cursor,
|
|
351
|
+
language: opts.language,
|
|
352
|
+
limit: parseLimit(opts.limit),
|
|
353
|
+
page: parsePage(opts.page),
|
|
354
|
+
search: opts.search,
|
|
355
|
+
sort: opts.sort,
|
|
356
|
+
tags: opts.tags,
|
|
357
|
+
},
|
|
358
|
+
asUserId: currentUser(program),
|
|
359
|
+
});
|
|
360
|
+
process.stdout.write(JSON.stringify(normalizedRoomList(data)) + "\n");
|
|
361
|
+
});
|
|
362
|
+
group
|
|
363
|
+
.command("trending")
|
|
364
|
+
.description("List trending open rooms with normalized JSON output.")
|
|
365
|
+
.option("--limit <n>", `page size, 1-${MAX_ROOM_LIMIT}`, String(DEFAULT_ROOM_LIMIT))
|
|
366
|
+
.action(async (opts) => {
|
|
367
|
+
const data = await call({
|
|
368
|
+
method: "GET",
|
|
369
|
+
path: "/api/v1/discover/rooms/enriched",
|
|
370
|
+
query: { sort: "trending", limit: parseLimit(opts.limit), page: 1 },
|
|
371
|
+
asUserId: currentUser(program),
|
|
372
|
+
});
|
|
373
|
+
process.stdout.write(JSON.stringify(normalizedRoomList(data)) + "\n");
|
|
374
|
+
});
|
|
375
|
+
group
|
|
376
|
+
.command("search")
|
|
377
|
+
.description("Search open rooms with normalized JSON output.")
|
|
378
|
+
.requiredOption("--q <query>", "room search query")
|
|
379
|
+
.option("--cursor <value>", "pagination cursor")
|
|
380
|
+
.option("--limit <n>", `page size, 1-${MAX_ROOM_LIMIT}`, String(DEFAULT_ROOM_LIMIT))
|
|
381
|
+
.action(async (opts) => {
|
|
382
|
+
const data = await call({
|
|
383
|
+
method: "GET",
|
|
384
|
+
path: "/api/v1/search",
|
|
385
|
+
query: { type: "rooms", q: opts.q, cursor: opts.cursor, limit: parseLimit(opts.limit) },
|
|
386
|
+
asUserId: currentUser(program),
|
|
387
|
+
});
|
|
388
|
+
process.stdout.write(JSON.stringify(normalizedRoomList(data)) + "\n");
|
|
389
|
+
});
|
|
390
|
+
addRoomShapeOptions(group
|
|
391
|
+
.command("create")
|
|
392
|
+
.description("Create a room with typed flags. Friendlier alias for create-room.")
|
|
393
|
+
.requiredOption("--name <name>", "room name")
|
|
394
|
+
.option("--language <value>", "room language")
|
|
395
|
+
.option("--install-agent-id <id>", "agent id to install on create"), { createOnly: true }).action(async (opts) => {
|
|
396
|
+
const data = await call({
|
|
397
|
+
method: "POST",
|
|
398
|
+
path: "/api/v1/rooms",
|
|
399
|
+
body: roomBody(opts, "create"),
|
|
400
|
+
asUserId: currentUser(program),
|
|
401
|
+
});
|
|
402
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
403
|
+
});
|
|
404
|
+
addRoomShapeOptions(group
|
|
405
|
+
.command("create-with-message")
|
|
406
|
+
.description("Create a room, then send the first text message. Returns both results.")
|
|
407
|
+
.requiredOption("--name <name>", "room name")
|
|
408
|
+
.requiredOption("--text <text>", "first message body")
|
|
409
|
+
.option("--language <value>", "room language")
|
|
410
|
+
.option("--install-agent-id <id>", "agent id to install on create"), { createOnly: true }).action(async (opts) => {
|
|
411
|
+
const asUserId = currentUser(program);
|
|
412
|
+
const room = await call({
|
|
413
|
+
method: "POST",
|
|
414
|
+
path: "/api/v1/rooms",
|
|
415
|
+
body: roomBody(opts, "create"),
|
|
416
|
+
asUserId,
|
|
417
|
+
});
|
|
418
|
+
const created = roomRecord(room);
|
|
419
|
+
const roomId = created.id ?? created.roomId;
|
|
420
|
+
if (typeof roomId !== "string") {
|
|
421
|
+
throw new Error("Room was created but response did not include id/roomId");
|
|
422
|
+
}
|
|
423
|
+
const message = await call({
|
|
424
|
+
method: "POST",
|
|
425
|
+
path: `/api/v1/rooms/${encodeURIComponent(roomId)}/messages`,
|
|
426
|
+
body: { content: opts.text, type: "text" },
|
|
427
|
+
asUserId,
|
|
428
|
+
});
|
|
429
|
+
process.stdout.write(JSON.stringify({ room: normalizedRoom(room), message }) + "\n");
|
|
430
|
+
});
|
|
431
|
+
addRoomShapeOptions(group
|
|
432
|
+
.command("update")
|
|
433
|
+
.description("Update room metadata, visibility, posting controls, agents, or icon URL.")
|
|
434
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
435
|
+
.option("--name <name>", "room name")
|
|
436
|
+
.option("--icon-url <url>", "room icon URL")).action(async (opts) => {
|
|
437
|
+
const data = await call({
|
|
438
|
+
method: "PATCH",
|
|
439
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}`,
|
|
440
|
+
body: roomBody(opts, "update"),
|
|
441
|
+
asUserId: currentUser(program),
|
|
442
|
+
});
|
|
443
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
444
|
+
});
|
|
445
|
+
group
|
|
446
|
+
.command("delete")
|
|
447
|
+
.description("Delete a room.")
|
|
448
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
449
|
+
.action(async (opts) => {
|
|
450
|
+
const data = await call({
|
|
451
|
+
method: "DELETE",
|
|
452
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}`,
|
|
453
|
+
asUserId: currentUser(program),
|
|
454
|
+
});
|
|
455
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
456
|
+
});
|
|
457
|
+
group
|
|
458
|
+
.command("join")
|
|
459
|
+
.description("Join a room by id, with optional join code or invite token.")
|
|
460
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
461
|
+
.option("--code <joinCode>", "join code")
|
|
462
|
+
.option("--invite-token <token>", "invite token")
|
|
463
|
+
.option("--ref <value>", "referral/source marker")
|
|
464
|
+
.action(async (opts) => {
|
|
465
|
+
const data = await call({
|
|
466
|
+
method: "POST",
|
|
467
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/join`,
|
|
468
|
+
body: { joinCode: opts.code, inviteToken: opts.inviteToken, ref: opts.ref },
|
|
469
|
+
asUserId: currentUser(program),
|
|
470
|
+
});
|
|
471
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
472
|
+
});
|
|
473
|
+
group
|
|
474
|
+
.command("resolve-invite")
|
|
475
|
+
.description("Resolve a room invite token before joining.")
|
|
476
|
+
.requiredOption("--invite-token <token>", "room invite token, with or without i- prefix")
|
|
477
|
+
.action(async (opts) => {
|
|
478
|
+
const inviteToken = normalizeInviteToken(opts.inviteToken);
|
|
479
|
+
const room = await call({
|
|
480
|
+
method: "GET",
|
|
481
|
+
path: `/api/v1/rooms/resolve-code/${encodeURIComponent(inviteToken)}`,
|
|
482
|
+
asUserId: currentUser(program),
|
|
483
|
+
});
|
|
484
|
+
process.stdout.write(JSON.stringify(inviteResolution(inviteToken, room)) + "\n");
|
|
485
|
+
});
|
|
486
|
+
group
|
|
487
|
+
.command("accept-invite")
|
|
488
|
+
.description("Accept a room invite token by resolving it and joining the target room.")
|
|
489
|
+
.requiredOption("--invite-token <token>", "room invite token, with or without i- prefix")
|
|
490
|
+
.action(async (opts) => {
|
|
491
|
+
const asUserId = currentUser(program);
|
|
492
|
+
const inviteToken = normalizeInviteToken(opts.inviteToken);
|
|
493
|
+
const room = await call({
|
|
494
|
+
method: "GET",
|
|
495
|
+
path: `/api/v1/rooms/resolve-code/${encodeURIComponent(inviteToken)}`,
|
|
496
|
+
asUserId,
|
|
497
|
+
});
|
|
498
|
+
const resolved = inviteResolution(inviteToken, room);
|
|
499
|
+
const roomId = resolved.roomId;
|
|
500
|
+
if (typeof roomId !== "string") {
|
|
501
|
+
throw new Error("Invite token does not resolve to a room");
|
|
502
|
+
}
|
|
503
|
+
const result = await call({
|
|
504
|
+
method: "POST",
|
|
505
|
+
path: `/api/v1/rooms/${encodeURIComponent(roomId)}/join`,
|
|
506
|
+
body: { inviteToken },
|
|
507
|
+
asUserId,
|
|
508
|
+
});
|
|
509
|
+
const joined = roomRecord(result);
|
|
510
|
+
process.stdout.write(JSON.stringify({
|
|
511
|
+
inviteToken,
|
|
512
|
+
roomId,
|
|
513
|
+
status: joined.type === "joinRequest" ? "pending_approval" : "joined",
|
|
514
|
+
result,
|
|
515
|
+
}) + "\n");
|
|
516
|
+
});
|
|
517
|
+
group
|
|
518
|
+
.command("leave")
|
|
519
|
+
.description("Leave a room.")
|
|
520
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
521
|
+
.action(async (opts) => {
|
|
522
|
+
const data = await call({
|
|
523
|
+
method: "POST",
|
|
524
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/leave`,
|
|
525
|
+
asUserId: currentUser(program),
|
|
526
|
+
});
|
|
527
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
528
|
+
});
|
|
529
|
+
group
|
|
530
|
+
.command("read")
|
|
531
|
+
.description("Mark a room as read.")
|
|
532
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
533
|
+
.action(async (opts) => {
|
|
534
|
+
const data = await call({
|
|
535
|
+
method: "POST",
|
|
536
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/read`,
|
|
537
|
+
asUserId: currentUser(program),
|
|
538
|
+
});
|
|
539
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
540
|
+
});
|
|
541
|
+
group
|
|
542
|
+
.command("members")
|
|
543
|
+
.description("List room members.")
|
|
544
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
545
|
+
.option("--cursor <value>", "pagination cursor")
|
|
546
|
+
.option("--limit <n>", "page size")
|
|
547
|
+
.option("--sort <value>", "sort order")
|
|
548
|
+
.action(async (opts) => {
|
|
549
|
+
const data = await call({
|
|
550
|
+
method: "GET",
|
|
551
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/members`,
|
|
552
|
+
query: { cursor: opts.cursor, limit: opts.limit, sort: opts.sort },
|
|
553
|
+
asUserId: currentUser(program),
|
|
554
|
+
});
|
|
555
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
556
|
+
});
|
|
557
|
+
group
|
|
558
|
+
.command("kick")
|
|
559
|
+
.description("Remove a user from a room.")
|
|
560
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
561
|
+
.requiredOption("--user <userId>", "user to remove")
|
|
562
|
+
.action(async (opts) => {
|
|
563
|
+
const data = await call({
|
|
564
|
+
method: "DELETE",
|
|
565
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/members/${encodeURIComponent(opts.user)}`,
|
|
566
|
+
asUserId: currentUser(program),
|
|
567
|
+
});
|
|
568
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
569
|
+
});
|
|
570
|
+
group
|
|
571
|
+
.command("join-requests")
|
|
572
|
+
.description("List room join requests.")
|
|
573
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
574
|
+
.option("--status <value>", "request status")
|
|
575
|
+
.option("--cursor <value>", "pagination cursor")
|
|
576
|
+
.option("--limit <n>", "page size")
|
|
577
|
+
.action(async (opts) => {
|
|
578
|
+
const data = await call({
|
|
579
|
+
method: "GET",
|
|
580
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/join-requests`,
|
|
581
|
+
query: { status: opts.status, cursor: opts.cursor, limit: opts.limit },
|
|
582
|
+
asUserId: currentUser(program),
|
|
583
|
+
});
|
|
584
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
585
|
+
});
|
|
586
|
+
group
|
|
587
|
+
.command("approve")
|
|
588
|
+
.description("Approve a room join request.")
|
|
589
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
590
|
+
.requiredOption("--request <requestId>", "join request id")
|
|
591
|
+
.action(async (opts) => {
|
|
592
|
+
const data = await call({
|
|
593
|
+
method: "POST",
|
|
594
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/join-requests/${encodeURIComponent(opts.request)}/approve`,
|
|
595
|
+
asUserId: currentUser(program),
|
|
596
|
+
});
|
|
597
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
598
|
+
});
|
|
599
|
+
group
|
|
600
|
+
.command("deny")
|
|
601
|
+
.description("Deny a room join request.")
|
|
602
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
603
|
+
.requiredOption("--request <requestId>", "join request id")
|
|
604
|
+
.action(async (opts) => {
|
|
605
|
+
const data = await call({
|
|
606
|
+
method: "POST",
|
|
607
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/join-requests/${encodeURIComponent(opts.request)}/deny`,
|
|
608
|
+
asUserId: currentUser(program),
|
|
609
|
+
});
|
|
610
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
611
|
+
});
|
|
612
|
+
group
|
|
613
|
+
.command("rotate-code")
|
|
614
|
+
.description("Regenerate a room join code.")
|
|
615
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
616
|
+
.action(async (opts) => {
|
|
617
|
+
const data = await call({
|
|
618
|
+
method: "POST",
|
|
619
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/regenerate-join-code`,
|
|
620
|
+
asUserId: currentUser(program),
|
|
621
|
+
});
|
|
622
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
623
|
+
});
|
|
624
|
+
group
|
|
625
|
+
.command("membership")
|
|
626
|
+
.description("Get the acting user's membership in a room.")
|
|
627
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
628
|
+
.action(async (opts) => {
|
|
629
|
+
const data = await call({
|
|
630
|
+
method: "GET",
|
|
631
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/membership`,
|
|
632
|
+
asUserId: currentUser(program),
|
|
633
|
+
});
|
|
634
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
635
|
+
});
|
|
636
|
+
group
|
|
637
|
+
.command("upload-icon-file")
|
|
638
|
+
.description("Upload an image file and set it as the room icon.")
|
|
639
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
640
|
+
.requiredOption("--file <path>", "local image file (png/jpg/jpeg/gif/webp, ≤5MB)")
|
|
641
|
+
.action(async (opts) => {
|
|
642
|
+
const ext = extname(opts.file).toLowerCase();
|
|
643
|
+
const contentType = IMAGE_MIME[ext];
|
|
644
|
+
if (!contentType) {
|
|
645
|
+
throw new Error(`Unsupported image extension '${ext}'. Use one of: ${Object.keys(IMAGE_MIME).join(", ")}`);
|
|
646
|
+
}
|
|
647
|
+
const data = await callMultipart({
|
|
648
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/icon`,
|
|
649
|
+
asUserId: currentUser(program),
|
|
650
|
+
file: {
|
|
651
|
+
fieldName: "icon",
|
|
652
|
+
fileName: basename(opts.file),
|
|
653
|
+
data: readFileSync(opts.file),
|
|
654
|
+
contentType,
|
|
655
|
+
},
|
|
656
|
+
});
|
|
657
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
658
|
+
});
|
|
659
|
+
group
|
|
660
|
+
.command("upload-image-file")
|
|
661
|
+
.description("Upload an image file to a room (multipart). Prints the GCS URL to stdout. " +
|
|
662
|
+
"Pair with `messages send-text --attachments <url>` to attach.")
|
|
663
|
+
.requiredOption("--room <roomId>", "target room id")
|
|
664
|
+
.requiredOption("--file <path>", "local image file (png/jpg/jpeg/gif/webp, ≤10MB)")
|
|
665
|
+
.action(async (opts) => {
|
|
666
|
+
const { as } = program.opts();
|
|
667
|
+
if (!as)
|
|
668
|
+
throw new Error("--as <userId> is required");
|
|
669
|
+
const ext = extname(opts.file).toLowerCase();
|
|
670
|
+
const contentType = IMAGE_MIME[ext];
|
|
671
|
+
if (!contentType) {
|
|
672
|
+
throw new Error(`Unsupported image extension '${ext}'. Use one of: ${Object.keys(IMAGE_MIME).join(", ")}`);
|
|
673
|
+
}
|
|
674
|
+
const data = await callMultipart({
|
|
675
|
+
path: `/api/v1/rooms/${encodeURIComponent(opts.room)}/images`,
|
|
676
|
+
asUserId: as,
|
|
677
|
+
file: {
|
|
678
|
+
fieldName: "image",
|
|
679
|
+
fileName: basename(opts.file),
|
|
680
|
+
data: readFileSync(opts.file),
|
|
681
|
+
contentType,
|
|
682
|
+
},
|
|
683
|
+
});
|
|
684
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
//# sourceMappingURL=rooms.js.map
|