commonswarm 0.1.53 → 0.1.55
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/cswarm.cjs +822 -52
- package/package.json +1 -1
package/cswarm.cjs
CHANGED
|
@@ -13502,6 +13502,7 @@ var require_main3 = __commonJS({
|
|
|
13502
13502
|
// src/cli.ts
|
|
13503
13503
|
var cli_exports = {};
|
|
13504
13504
|
__export(cli_exports, {
|
|
13505
|
+
CHANNEL_SUBCOMMAND_NAMES: () => CHANNEL_SUBCOMMAND_NAMES,
|
|
13505
13506
|
EXIT_RESTARTABLE: () => EXIT_RESTARTABLE,
|
|
13506
13507
|
ListenerUnattendedRefusedError: () => ListenerUnattendedRefusedError,
|
|
13507
13508
|
TURN_BUDGET_CREDENTIAL_MARGIN_MS: () => TURN_BUDGET_CREDENTIAL_MARGIN_MS,
|
|
@@ -13519,7 +13520,9 @@ __export(cli_exports, {
|
|
|
13519
13520
|
replyRefusalHint: () => replyRefusalHint,
|
|
13520
13521
|
resolveDetachedClaudeExecutable: () => resolveDetachedClaudeExecutable,
|
|
13521
13522
|
resolveDetachedCodexExecutable: () => resolveDetachedCodexExecutable,
|
|
13522
|
-
resolveTurnBudgetOrDefer: () => resolveTurnBudgetOrDefer
|
|
13523
|
+
resolveTurnBudgetOrDefer: () => resolveTurnBudgetOrDefer,
|
|
13524
|
+
threadReplyMessage: () => threadReplyMessage,
|
|
13525
|
+
usage: () => usage
|
|
13523
13526
|
});
|
|
13524
13527
|
module.exports = __toCommonJS(cli_exports);
|
|
13525
13528
|
var import_node_crypto22 = require("node:crypto");
|
|
@@ -22141,11 +22144,217 @@ async function logout(target2, store2, scope = "local", options = {}) {
|
|
|
22141
22144
|
|
|
22142
22145
|
// src/cloud/command-client.ts
|
|
22143
22146
|
var import_node_crypto3 = require("node:crypto");
|
|
22147
|
+
|
|
22148
|
+
// src/cloud/channels.ts
|
|
22149
|
+
var CHANNEL_SLUG_MAX = 32;
|
|
22150
|
+
var CHANNEL_PURPOSE_MAX = 500;
|
|
22151
|
+
var CHANNEL_SLUG_CLASSES = {
|
|
22152
|
+
edge: [
|
|
22153
|
+
{ fragment: "a-z", words: "lowercase letters", one: "a letter" },
|
|
22154
|
+
{ fragment: "0-9", words: "digits", one: "a digit" }
|
|
22155
|
+
],
|
|
22156
|
+
inner: [{ fragment: "-", words: "hyphens" }]
|
|
22157
|
+
};
|
|
22158
|
+
function classList(entries, conjunction = "and", singular = false) {
|
|
22159
|
+
const words = entries.map(
|
|
22160
|
+
(entry) => singular ? entry.one ?? entry.words : entry.words
|
|
22161
|
+
);
|
|
22162
|
+
return words.length === 1 ? words[0] : `${words.slice(0, -1).join(", ")} ${conjunction} ${words[words.length - 1]}`;
|
|
22163
|
+
}
|
|
22164
|
+
var SLUG_EDGE = CHANNEL_SLUG_CLASSES.edge.map((c) => c.fragment).join("");
|
|
22165
|
+
var SLUG_INNER = SLUG_EDGE + CHANNEL_SLUG_CLASSES.inner.map((c) => c.fragment).join("");
|
|
22166
|
+
var CHANNEL_SLUG_RE = new RegExp(
|
|
22167
|
+
`^[${SLUG_EDGE}]([${SLUG_INNER}]*[${SLUG_EDGE}])?$`
|
|
22168
|
+
);
|
|
22169
|
+
var RESERVED_CHANNEL_SLUGS = ["all-signals"];
|
|
22170
|
+
var CHANNEL_SLUG_RULE_TEXT = `A channel name uses ${classList([...CHANNEL_SLUG_CLASSES.edge, ...CHANNEL_SLUG_CLASSES.inner])}, starts and ends with ${classList(CHANNEL_SLUG_CLASSES.edge, "or", true)}, and is 1 to ${CHANNEL_SLUG_MAX} characters.`;
|
|
22171
|
+
var CHANNEL_ID_RULE_TEXT = "channel_id must be a UUID.";
|
|
22172
|
+
var RESERVED_CHANNEL_SLUG_TEXT = `Reserved names: ${RESERVED_CHANNEL_SLUGS.join(", ")}.`;
|
|
22173
|
+
function normalizeChannelSlug(value) {
|
|
22174
|
+
return value.trim().toLowerCase();
|
|
22175
|
+
}
|
|
22176
|
+
function isReservedChannelSlug(value) {
|
|
22177
|
+
return RESERVED_CHANNEL_SLUGS.includes(normalizeChannelSlug(value));
|
|
22178
|
+
}
|
|
22179
|
+
function channelNameProblem(value) {
|
|
22180
|
+
if (typeof value !== "string") return "not-text";
|
|
22181
|
+
const slug = normalizeChannelSlug(value);
|
|
22182
|
+
if (slug.length < 1 || slug.length > CHANNEL_SLUG_MAX) return "shape";
|
|
22183
|
+
if (!CHANNEL_SLUG_RE.test(slug)) return "shape";
|
|
22184
|
+
if (isReservedChannelSlug(slug)) return "reserved";
|
|
22185
|
+
return "ok";
|
|
22186
|
+
}
|
|
22187
|
+
function channelSlugProblem(value) {
|
|
22188
|
+
switch (channelNameProblem(value)) {
|
|
22189
|
+
case "ok":
|
|
22190
|
+
return null;
|
|
22191
|
+
case "not-text":
|
|
22192
|
+
return `A channel name must be text. ${CHANNEL_SLUG_RULE_TEXT}`;
|
|
22193
|
+
case "shape":
|
|
22194
|
+
return CHANNEL_SLUG_RULE_TEXT;
|
|
22195
|
+
case "reserved":
|
|
22196
|
+
return `${normalizeChannelSlug(value)} is reserved. ${RESERVED_CHANNEL_SLUG_TEXT}`;
|
|
22197
|
+
}
|
|
22198
|
+
}
|
|
22199
|
+
var CHANNEL_COLUMNS = [
|
|
22200
|
+
"channel_id",
|
|
22201
|
+
"workspace_id",
|
|
22202
|
+
"slug",
|
|
22203
|
+
"purpose",
|
|
22204
|
+
"created_by_principal",
|
|
22205
|
+
"created_by_kind",
|
|
22206
|
+
"created_at",
|
|
22207
|
+
"archived_at"
|
|
22208
|
+
];
|
|
22209
|
+
var CHANNEL_LIST_NEEDS_HUMAN_MESSAGE = "Listing channels needs a signed-in person: this deployment's read service has no channel list for an agent credential. Run cswarm channel ls from a session signed in with cswarm login, or name the channel you want by name wherever a command takes one.";
|
|
22210
|
+
var CHANNEL_SELECTOR_NEEDS_ID_MESSAGE = "Turning a channel name into a channel id needs a signed-in person, because this deployment's read service does not list channels for an agent credential. Pass the channel id instead. cswarm channel create prints it, and cswarm channel ls shows it from a session signed in with cswarm login.";
|
|
22211
|
+
function channelSelectorProblem(selector) {
|
|
22212
|
+
const problem = channelNameProblem(selector);
|
|
22213
|
+
if (problem === "ok") return null;
|
|
22214
|
+
if (problem === "reserved") return channelSlugProblem(selector);
|
|
22215
|
+
return `That is neither a channel name nor a channel id. ${CHANNEL_SLUG_RULE_TEXT} ${CHANNEL_ID_RULE_TEXT}`;
|
|
22216
|
+
}
|
|
22217
|
+
var ChannelListError = class extends Error {
|
|
22218
|
+
constructor(status, message, noResponse = false) {
|
|
22219
|
+
super(message);
|
|
22220
|
+
this.status = status;
|
|
22221
|
+
this.noResponse = noResponse;
|
|
22222
|
+
this.name = "ChannelListError";
|
|
22223
|
+
}
|
|
22224
|
+
status;
|
|
22225
|
+
noResponse;
|
|
22226
|
+
};
|
|
22227
|
+
async function listChannelsAsHuman(target2, accessToken, workspaceId2, fetcher = fetch, timeoutMs = 3e4) {
|
|
22228
|
+
const url = new URL("/rest/v1/channels", target2.url);
|
|
22229
|
+
url.searchParams.set("workspace_id", `eq.${workspaceId2}`);
|
|
22230
|
+
url.searchParams.set("select", CHANNEL_COLUMNS.join(","));
|
|
22231
|
+
url.searchParams.set("order", "slug.asc");
|
|
22232
|
+
const controller = new AbortController();
|
|
22233
|
+
const timer2 = setTimeout(() => controller.abort(), timeoutMs);
|
|
22234
|
+
let response;
|
|
22235
|
+
try {
|
|
22236
|
+
response = await fetcher(url.toString(), {
|
|
22237
|
+
headers: {
|
|
22238
|
+
authorization: `Bearer ${accessToken}`,
|
|
22239
|
+
apikey: target2.anonKey,
|
|
22240
|
+
"accept-profile": "swarm_read"
|
|
22241
|
+
},
|
|
22242
|
+
signal: controller.signal
|
|
22243
|
+
});
|
|
22244
|
+
} catch {
|
|
22245
|
+
throw new ChannelListError(
|
|
22246
|
+
0,
|
|
22247
|
+
"The channel list did not complete. Nothing changed. Run the same command again.",
|
|
22248
|
+
true
|
|
22249
|
+
);
|
|
22250
|
+
} finally {
|
|
22251
|
+
clearTimeout(timer2);
|
|
22252
|
+
}
|
|
22253
|
+
if (!response.ok) {
|
|
22254
|
+
throw new ChannelListError(
|
|
22255
|
+
response.status,
|
|
22256
|
+
`The channel list was refused (HTTP ${response.status}). Nothing changed.`
|
|
22257
|
+
);
|
|
22258
|
+
}
|
|
22259
|
+
let body = null;
|
|
22260
|
+
try {
|
|
22261
|
+
body = await response.json();
|
|
22262
|
+
} catch {
|
|
22263
|
+
body = null;
|
|
22264
|
+
}
|
|
22265
|
+
if (!Array.isArray(body)) {
|
|
22266
|
+
throw new ChannelListError(
|
|
22267
|
+
response.status,
|
|
22268
|
+
"The channel list came back in a shape this version does not understand."
|
|
22269
|
+
);
|
|
22270
|
+
}
|
|
22271
|
+
return body;
|
|
22272
|
+
}
|
|
22273
|
+
function findChannelBySlug(rows3, slug) {
|
|
22274
|
+
const wanted = normalizeChannelSlug(slug);
|
|
22275
|
+
return rows3.find((row) => normalizeChannelSlug(row.slug) === wanted) ?? null;
|
|
22276
|
+
}
|
|
22277
|
+
function unknownChannelMessage(slug, rows3) {
|
|
22278
|
+
const live = rows3.filter((row) => row.archived_at === null).map((row) => row.slug).sort();
|
|
22279
|
+
return live.length === 0 ? `There is no channel named ${normalizeChannelSlug(slug)} in this workspace, and no channel has been created yet. Create it with cswarm channel create ${normalizeChannelSlug(slug)}.` : `There is no channel named ${normalizeChannelSlug(slug)} in this workspace. Channels here: ${live.join(", ")}.`;
|
|
22280
|
+
}
|
|
22281
|
+
function renderChannelList(rows3, options) {
|
|
22282
|
+
const live = rows3.filter((row) => row.archived_at === null);
|
|
22283
|
+
const archived = rows3.filter((row) => row.archived_at !== null);
|
|
22284
|
+
const shown = options.includeArchived ? [...live, ...archived] : live;
|
|
22285
|
+
if (shown.length === 0) {
|
|
22286
|
+
return live.length === 0 && archived.length > 0 ? "Every channel in this workspace is archived. See them with cswarm channel ls --include-archived.\n" : "No channels in this workspace yet. Create one with cswarm channel create <name>.\n";
|
|
22287
|
+
}
|
|
22288
|
+
const lines = shown.map((row) => {
|
|
22289
|
+
const marker = row.archived_at === null ? "" : " [archived; it keeps its history and takes no new messages]";
|
|
22290
|
+
const purpose = row.purpose === null ? "" : `: ${row.purpose}`;
|
|
22291
|
+
return `- ${row.slug}${purpose}${marker}`;
|
|
22292
|
+
});
|
|
22293
|
+
const head2 = `Channels in this workspace (${shown.length}):`;
|
|
22294
|
+
const tail = options.includeArchived || archived.length === 0 ? "" : `
|
|
22295
|
+
${archived.length} archived channel${archived.length === 1 ? "" : "s"} not shown. See them with cswarm channel ls --include-archived.`;
|
|
22296
|
+
return `${head2}
|
|
22297
|
+
${lines.join("\n")}${tail}
|
|
22298
|
+
`;
|
|
22299
|
+
}
|
|
22300
|
+
var CHANNEL_UNSUPPORTED_MESSAGE = `This deployment refused the request and did not say why. Channels need a deployment whose command service knows them; this cswarm speaks protocol ${CLIENT_PROTOCOL_VERSION}. Nothing was created or changed. Ask whoever runs this deployment to update it, or drop the channel options and post as before.`;
|
|
22301
|
+
|
|
22302
|
+
// src/cloud/command-client.ts
|
|
22144
22303
|
var AGENT_TOKEN_RE = /^swm_agt_[A-Za-z0-9_-]{43}$/;
|
|
22145
22304
|
var INVITATION_TOKEN_RE = /^swm_inv_[A-Za-z0-9_-]{43}$/;
|
|
22146
22305
|
var CAPABILITY_TOKEN_RE = /^swm_cap_[A-Za-z0-9_-]{43}$/;
|
|
22147
22306
|
var CONTROL_RE = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/;
|
|
22148
22307
|
var WORKSPACE_NAME_MAX_LENGTH = 80;
|
|
22308
|
+
var ChannelCommandError = class extends Error {
|
|
22309
|
+
constructor(status, code, message) {
|
|
22310
|
+
super(message);
|
|
22311
|
+
this.status = status;
|
|
22312
|
+
this.code = code;
|
|
22313
|
+
this.name = "ChannelCommandError";
|
|
22314
|
+
}
|
|
22315
|
+
status;
|
|
22316
|
+
code;
|
|
22317
|
+
};
|
|
22318
|
+
function channelCommandError(status, body) {
|
|
22319
|
+
const record = body && typeof body === "object" && !Array.isArray(body) ? body : {};
|
|
22320
|
+
const code = typeof record.error === "string" ? record.error : "unknown";
|
|
22321
|
+
const served = typeof record.message === "string" && record.message.length > 0 ? record.message.slice(0, 600) : null;
|
|
22322
|
+
if (served !== null) return new ChannelCommandError(status, code, served);
|
|
22323
|
+
if (status === 426) {
|
|
22324
|
+
const minimum = typeof record.min_client_version === "string" ? record.min_client_version : null;
|
|
22325
|
+
return new ChannelCommandError(
|
|
22326
|
+
status,
|
|
22327
|
+
"upgrade_required",
|
|
22328
|
+
`This copy of cswarm is older than the deployment accepts${minimum === null ? "" : ` (minimum ${minimum})`}. Update cswarm, then run the same command again. Nothing changed.`
|
|
22329
|
+
);
|
|
22330
|
+
}
|
|
22331
|
+
if (status === 403) {
|
|
22332
|
+
return new ChannelCommandError(
|
|
22333
|
+
status,
|
|
22334
|
+
code === "unknown" ? "forbidden" : code,
|
|
22335
|
+
"This credential may not do that in this workspace. Nothing changed."
|
|
22336
|
+
);
|
|
22337
|
+
}
|
|
22338
|
+
if (status === 401) {
|
|
22339
|
+
return new ChannelCommandError(
|
|
22340
|
+
status,
|
|
22341
|
+
code === "unknown" ? "unauthenticated" : code,
|
|
22342
|
+
"Your sign-in is no longer valid for this deployment. Run cswarm login, then run the same command again. Nothing changed."
|
|
22343
|
+
);
|
|
22344
|
+
}
|
|
22345
|
+
if (status === 400) {
|
|
22346
|
+
return new ChannelCommandError(
|
|
22347
|
+
status,
|
|
22348
|
+
code === "unknown" ? "invalid_request" : code,
|
|
22349
|
+
CHANNEL_UNSUPPORTED_MESSAGE
|
|
22350
|
+
);
|
|
22351
|
+
}
|
|
22352
|
+
return new ChannelCommandError(
|
|
22353
|
+
status,
|
|
22354
|
+
code,
|
|
22355
|
+
`CommonSwarm could not tell whether the change was made (HTTP ${status}). Run cswarm channel ls to see the current channels before trying again.`
|
|
22356
|
+
);
|
|
22357
|
+
}
|
|
22149
22358
|
var CAPABILITY_MIN_TTL_MS = 6e4;
|
|
22150
22359
|
var CAPABILITY_MAX_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
22151
22360
|
var SIGNAL_REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -22705,6 +22914,80 @@ var ThinCommandClient = class {
|
|
|
22705
22914
|
}
|
|
22706
22915
|
return { httpStatus: response.status, response: body };
|
|
22707
22916
|
}
|
|
22917
|
+
/**
|
|
22918
|
+
* Create, rename, or archive a channel.
|
|
22919
|
+
*
|
|
22920
|
+
* Deliberately not folded into `sendConnect`. That path throws a bare
|
|
22921
|
+
* `CommandHttpError(403)` and, on any other refusal, reads only the `error`
|
|
22922
|
+
* code — so every generated sentence the chat validator returns would be
|
|
22923
|
+
* thrown away at the one moment the caller needs it. This method reads the
|
|
22924
|
+
* body once and hands it to `channelCommandError`.
|
|
22925
|
+
*/
|
|
22926
|
+
async sendChannel(request) {
|
|
22927
|
+
if (!request.workspaceId) {
|
|
22928
|
+
throw new Error("workspaceId is required for a channel command");
|
|
22929
|
+
}
|
|
22930
|
+
const commandId = request.commandId ?? newCommandId();
|
|
22931
|
+
const controller = new AbortController();
|
|
22932
|
+
const timer2 = setTimeout(() => controller.abort(), 3e4);
|
|
22933
|
+
let response;
|
|
22934
|
+
try {
|
|
22935
|
+
response = await this.fetcher(commandEndpoint(this.target), {
|
|
22936
|
+
method: "POST",
|
|
22937
|
+
headers: {
|
|
22938
|
+
authorization: `Bearer ${request.credential}`,
|
|
22939
|
+
apikey: this.target.anonKey,
|
|
22940
|
+
"content-type": "application/json"
|
|
22941
|
+
},
|
|
22942
|
+
body: JSON.stringify({
|
|
22943
|
+
command_id: commandId,
|
|
22944
|
+
client_version: CLIENT_PROTOCOL_VERSION,
|
|
22945
|
+
workspace_id: request.workspaceId,
|
|
22946
|
+
stream: { kind: "workspace" },
|
|
22947
|
+
command: request.command
|
|
22948
|
+
}),
|
|
22949
|
+
signal: controller.signal
|
|
22950
|
+
});
|
|
22951
|
+
} catch (error) {
|
|
22952
|
+
if (error.name === "AbortError") {
|
|
22953
|
+
throw new CommandTransportError("channel request timed out");
|
|
22954
|
+
}
|
|
22955
|
+
throw new CommandTransportError(
|
|
22956
|
+
"channel request failed before a response"
|
|
22957
|
+
);
|
|
22958
|
+
} finally {
|
|
22959
|
+
clearTimeout(timer2);
|
|
22960
|
+
}
|
|
22961
|
+
let raw = null;
|
|
22962
|
+
try {
|
|
22963
|
+
raw = await parsedJson(response);
|
|
22964
|
+
} catch (error) {
|
|
22965
|
+
if (response.ok || error instanceof CommandTransportError) throw error;
|
|
22966
|
+
}
|
|
22967
|
+
if (!response.ok) throw channelCommandError(response.status, raw);
|
|
22968
|
+
const body = responseBody(raw);
|
|
22969
|
+
if (body.min_client_version !== void 0) {
|
|
22970
|
+
const order = compareVersion(
|
|
22971
|
+
CLIENT_PROTOCOL_VERSION,
|
|
22972
|
+
body.min_client_version
|
|
22973
|
+
);
|
|
22974
|
+
if (order === null) {
|
|
22975
|
+
throw new Error("server returned a malformed min_client_version");
|
|
22976
|
+
}
|
|
22977
|
+
if (order < 0) {
|
|
22978
|
+
throw new Error(
|
|
22979
|
+
`client upgrade required (minimum ${body.min_client_version})`
|
|
22980
|
+
);
|
|
22981
|
+
}
|
|
22982
|
+
}
|
|
22983
|
+
const channel = raw && typeof raw === "object" && !Array.isArray(raw) ? raw.channel : null;
|
|
22984
|
+
if (channel === null || channel === void 0 || typeof channel.channel_id !== "string" || typeof channel.slug !== "string") {
|
|
22985
|
+
throw new Error(
|
|
22986
|
+
"the deployment accepted the change without saying which channel it applies to"
|
|
22987
|
+
);
|
|
22988
|
+
}
|
|
22989
|
+
return { httpStatus: response.status, response: body, channel };
|
|
22990
|
+
}
|
|
22708
22991
|
async sendSignal(request) {
|
|
22709
22992
|
const commandId = request.commandId ?? newCommandId();
|
|
22710
22993
|
const command2 = {
|
|
@@ -22716,7 +22999,17 @@ var ThinCommandClient = class {
|
|
|
22716
22999
|
in_reply_to: request.command.in_reply_to,
|
|
22717
23000
|
about: request.command.about,
|
|
22718
23001
|
...request.command.attachments === void 0 ? {} : { attachments: request.command.attachments },
|
|
22719
|
-
...request.command.until_ms === void 0 ? {} : { until_ms: request.command.until_ms }
|
|
23002
|
+
...request.command.until_ms === void 0 ? {} : { until_ms: request.command.until_ms },
|
|
23003
|
+
/* One spread per chat key, never a shared group. The edge reads each with
|
|
23004
|
+
* its own Object.hasOwn and refuses any key it did not expect, so sending
|
|
23005
|
+
* `channel: undefined` here would still put the key on the wire through
|
|
23006
|
+
* JSON.stringify's own omission rules only by accident — and sending a
|
|
23007
|
+
* null placeholder, the way to_user_id is sent, would make every post
|
|
23008
|
+
* demand a channel. This rebuild is also the reason the fields have to be
|
|
23009
|
+
* listed here at all: it drops anything it does not name. */
|
|
23010
|
+
...request.command.channel === void 0 ? {} : { channel: request.command.channel },
|
|
23011
|
+
...request.command.thread_root_id === void 0 ? {} : { thread_root_id: request.command.thread_root_id },
|
|
23012
|
+
...request.command.broadcast_to_channel === void 0 ? {} : { broadcast_to_channel: request.command.broadcast_to_channel }
|
|
22720
23013
|
};
|
|
22721
23014
|
const callerSignal = request.signal;
|
|
22722
23015
|
if (callerSignal?.aborted) {
|
|
@@ -29040,6 +29333,12 @@ function checkedUuid2(value, field) {
|
|
|
29040
29333
|
function checkedNullableUuid(value, field) {
|
|
29041
29334
|
return value === null ? null : checkedUuid2(value, field);
|
|
29042
29335
|
}
|
|
29336
|
+
function checkedBoolean(value, field) {
|
|
29337
|
+
if (typeof value !== "boolean") {
|
|
29338
|
+
throw new Error(`signal read returned a malformed ${field}`);
|
|
29339
|
+
}
|
|
29340
|
+
return value;
|
|
29341
|
+
}
|
|
29043
29342
|
function checkedTimestamp(value, field) {
|
|
29044
29343
|
if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) {
|
|
29045
29344
|
throw new Error(`signal read returned a malformed ${field}`);
|
|
@@ -29119,7 +29418,29 @@ function parseSignalRecord(value, options = {}) {
|
|
|
29119
29418
|
}),
|
|
29120
29419
|
until: checkedTimestamp(row.until, "until"),
|
|
29121
29420
|
created_at: checkedTimestamp(row.created_at, "created_at"),
|
|
29122
|
-
sender_owner_relation: senderOwnerRelation
|
|
29421
|
+
sender_owner_relation: senderOwnerRelation,
|
|
29422
|
+
/* ABSENT AND NULL ARE DIFFERENT HERE, and the difference is a claim.
|
|
29423
|
+
*
|
|
29424
|
+
* `null` means the server said this signal is in no channel. Absent means
|
|
29425
|
+
* this reader never asked: the human REST path names the chat columns only
|
|
29426
|
+
* when a channel filter is set, and an edge that predates channels never
|
|
29427
|
+
* returns them. Normalizing absence to null, the way `to_agent` above does,
|
|
29428
|
+
* would put `"channel_id": null` in `cswarm feed --json` for a signal that
|
|
29429
|
+
* IS filed in a channel — a false statement, not a missing one. So an
|
|
29430
|
+
* absent key stays absent, and a present one is checked. */
|
|
29431
|
+
...row.channel_id === void 0 ? {} : { channel_id: checkedNullableUuid(row.channel_id, "channel_id") },
|
|
29432
|
+
...row.thread_root_id === void 0 ? {} : {
|
|
29433
|
+
thread_root_id: checkedNullableUuid(
|
|
29434
|
+
row.thread_root_id,
|
|
29435
|
+
"thread_root_id"
|
|
29436
|
+
)
|
|
29437
|
+
},
|
|
29438
|
+
...row.broadcast_to_channel === void 0 ? {} : {
|
|
29439
|
+
broadcast_to_channel: checkedBoolean(
|
|
29440
|
+
row.broadcast_to_channel,
|
|
29441
|
+
"broadcast_to_channel"
|
|
29442
|
+
)
|
|
29443
|
+
}
|
|
29123
29444
|
};
|
|
29124
29445
|
}
|
|
29125
29446
|
function cursorFromUnknown(value) {
|
|
@@ -29431,9 +29752,15 @@ async function humanSignals(target2, credential, query, options) {
|
|
|
29431
29752
|
const url = new URL("/rest/v1/signals", target2.url);
|
|
29432
29753
|
url.searchParams.set(
|
|
29433
29754
|
"select",
|
|
29434
|
-
|
|
29755
|
+
[
|
|
29756
|
+
"id,workspace_id,from,from_kind,to,to_agent,in_reply_to,about,kind,body,attachments,until,created_at",
|
|
29757
|
+
...query.channelId === void 0 ? [] : ["channel_id", "thread_root_id", "broadcast_to_channel"]
|
|
29758
|
+
].join(",")
|
|
29435
29759
|
);
|
|
29436
29760
|
url.searchParams.set("workspace_id", `eq.${query.workspaceId}`);
|
|
29761
|
+
if (query.channelId !== void 0) {
|
|
29762
|
+
url.searchParams.set("channel_id", `eq.${query.channelId}`);
|
|
29763
|
+
}
|
|
29437
29764
|
if (query.inbox) url.searchParams.set("to", `eq.${credential.userId}`);
|
|
29438
29765
|
if (!query.includeStale) {
|
|
29439
29766
|
url.searchParams.set("until", "gt.now");
|
|
@@ -29506,6 +29833,11 @@ async function agentSignalPage(target2, credential, query, options, allowLegacyC
|
|
|
29506
29833
|
about: query.about ?? null,
|
|
29507
29834
|
kind: query.kind ?? null,
|
|
29508
29835
|
in_reply_to: query.in_reply_to ?? null,
|
|
29836
|
+
/* Its OWN key, present only when asked for. The read edge groups it
|
|
29837
|
+
* with `chatReadKeys` and refuses a key it did not expect, and every
|
|
29838
|
+
* agent body already carries `in_reply_to`, so folding `channel` in
|
|
29839
|
+
* beside it would 400 every agent read that omits a channel. */
|
|
29840
|
+
...query.channel === void 0 ? {} : { channel: query.channel },
|
|
29509
29841
|
since: query.since ?? null,
|
|
29510
29842
|
...includeCursor ? {
|
|
29511
29843
|
after_created_at: query.after?.created_at ?? null,
|
|
@@ -34518,6 +34850,25 @@ async function resolveBudgetAndPrompt(session, prompt, budget) {
|
|
|
34518
34850
|
const timeoutMs = typeof budget === "number" ? budget : await budget();
|
|
34519
34851
|
return await session.prompt(prompt, { timeoutMs });
|
|
34520
34852
|
}
|
|
34853
|
+
var LISTENER_DELIVERY_MAX_LEASE_MS = 9e5;
|
|
34854
|
+
var LISTENER_DELIVERY_HOLD_RELEASE_REASONS = [
|
|
34855
|
+
"hold_budget",
|
|
34856
|
+
"lease_budget"
|
|
34857
|
+
];
|
|
34858
|
+
var LISTENER_DELIVERY_HOLD_RELEASE_CLAUSES = {
|
|
34859
|
+
hold_budget: "it used the turn budget for one delivery",
|
|
34860
|
+
lease_budget: "what was left of its lease could not cover the next step"
|
|
34861
|
+
};
|
|
34862
|
+
var LISTENER_DELIVERY_HOLD_RELEASE_REMEDIES = {
|
|
34863
|
+
hold_budget: `a larger --turn-budget gives one delivery more of the seat. Past the ${LISTENER_DELIVERY_MAX_LEASE_MS / 6e4} minutes the service leases a delivery for it stops helping, because the turn then outlives its lease and the reply can no longer be acknowledged. The bound is read when the listener starts, so stop this listener and start it again to change it`,
|
|
34864
|
+
/* NOT a cap: nothing clamps the turn budget to the lease, and leaseSpent
|
|
34865
|
+
refuses to START a phase rather than interrupting one, so a 60m budget
|
|
34866
|
+
really does hold the worker for 60m. The sentence says raising past the
|
|
34867
|
+
lease stops helping, and why, which is what the code supports. An earlier
|
|
34868
|
+
version read "up to the 15 minutes the service leases it for", which a
|
|
34869
|
+
review arm read as a cap the code does not enforce. */
|
|
34870
|
+
lease_budget: "nothing needs raising: the row comes back under a new lease of full length, so the next attempt starts with the room this one ran out of. If it keeps being handed back, the service stops retrying it in the end, so look at the delivery rather than at the bound"
|
|
34871
|
+
};
|
|
34521
34872
|
|
|
34522
34873
|
// src/listener/engine.ts
|
|
34523
34874
|
var UUID_RE14 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
@@ -36739,11 +37090,11 @@ function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
|
|
|
36739
37090
|
// src/listener/runtime.ts
|
|
36740
37091
|
var LISTENER_PAGE_LIMIT = 100;
|
|
36741
37092
|
var LISTENER_IDLE_POLL_MS = 2e3;
|
|
36742
|
-
var LISTENER_DELIVERY_MAX_LEASE_MS = 9e5;
|
|
36743
37093
|
var LISTENER_DELIVERY_SAFETY_MARGIN_MS = 3e4;
|
|
36744
37094
|
var LISTENER_ACK_ONLY_MINIMUM_MS = DELIVERY_REQUEST_TIMEOUT_MS + LISTENER_DELIVERY_SAFETY_MARGIN_MS;
|
|
36745
37095
|
var LISTENER_REPLY_ONLY_MINIMUM_MS = SIGNAL_REQUEST_TIMEOUT_MS + LISTENER_ACK_ONLY_MINIMUM_MS;
|
|
36746
37096
|
var LISTENER_PROMPT_START_MINIMUM_MS = SIGNAL_READ_TIMEOUT_MS + ACP_DEFAULT_REQUEST_TIMEOUT_MS + LISTENER_REPLY_ONLY_MINIMUM_MS;
|
|
37097
|
+
var LISTENER_DELIVERY_HOLD_BUDGET_MS = LISTENER_PROMPT_TIMEOUT_MS;
|
|
36747
37098
|
var LISTENER_DELIVERY_RETRY_INITIAL_MS = 500;
|
|
36748
37099
|
var LISTENER_DELIVERY_RETRY_MAX_MS = 3e4;
|
|
36749
37100
|
var LISTENER_HOST_PORTS_PROBE_MS = 6e4;
|
|
@@ -37016,6 +37367,7 @@ async function runListenerRuntime(options) {
|
|
|
37016
37367
|
const pollMs = options.pollMs ?? LISTENER_IDLE_POLL_MS;
|
|
37017
37368
|
const routeMode = options.routeMode ?? "worker";
|
|
37018
37369
|
const deferOverChars = options.deferOverChars ?? null;
|
|
37370
|
+
const deliveryHoldBudgetMs = options.deliveryHoldBudgetMs ?? LISTENER_DELIVERY_HOLD_BUDGET_MS;
|
|
37019
37371
|
const abort = options.signal;
|
|
37020
37372
|
const hasInstanceId = options.listenerInstanceId !== void 0;
|
|
37021
37373
|
const hasJournal = options.deliveryJournal !== void 0;
|
|
@@ -37037,6 +37389,12 @@ async function runListenerRuntime(options) {
|
|
|
37037
37389
|
new Error("an injected delivery client requires durable delivery configuration")
|
|
37038
37390
|
);
|
|
37039
37391
|
}
|
|
37392
|
+
if (!Number.isSafeInteger(deliveryHoldBudgetMs) || deliveryHoldBudgetMs <= 0) {
|
|
37393
|
+
return await closeBeforeStart(
|
|
37394
|
+
options.model,
|
|
37395
|
+
new Error("listener delivery hold budget must be a positive number of milliseconds")
|
|
37396
|
+
);
|
|
37397
|
+
}
|
|
37040
37398
|
try {
|
|
37041
37399
|
decideListenerRoute(routeMode, deferOverChars, 0);
|
|
37042
37400
|
if (routeMode !== "worker" && options.pendingMainQueue === void 0) {
|
|
@@ -37630,6 +37988,8 @@ async function runListenerRuntime(options) {
|
|
|
37630
37988
|
stop = { reason: "cancelled" };
|
|
37631
37989
|
break;
|
|
37632
37990
|
}
|
|
37991
|
+
const claimedAtMs = Date.parse(active.claimCreatedAt);
|
|
37992
|
+
const holdStartedAtMs = Number.isFinite(claimedAtMs) ? Math.min(claimedAtMs, now()) : now();
|
|
37633
37993
|
const signal = authoritativeSignal(claimed);
|
|
37634
37994
|
let terminal = null;
|
|
37635
37995
|
try {
|
|
@@ -37696,22 +38056,18 @@ async function runListenerRuntime(options) {
|
|
|
37696
38056
|
throw new Error("stored listener effect does not match the authoritative delivery");
|
|
37697
38057
|
}
|
|
37698
38058
|
const requiredBudget = effectPhaseBudget(before);
|
|
37699
|
-
|
|
37700
|
-
|
|
37701
|
-
|
|
37702
|
-
|
|
37703
|
-
|
|
37704
|
-
|
|
37705
|
-
|
|
37706
|
-
|
|
37707
|
-
|
|
37708
|
-
|
|
37709
|
-
|
|
37710
|
-
}
|
|
37711
|
-
if (now() >= leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS) {
|
|
37712
|
-
await journal.clearActive(eventTime(now));
|
|
37713
|
-
after = null;
|
|
37714
|
-
}
|
|
38059
|
+
const holdSpent = processAttempt > 0 && now() - holdStartedAtMs >= deliveryHoldBudgetMs;
|
|
38060
|
+
const leaseSpent = leasedUntilMs <= now() + requiredBudget;
|
|
38061
|
+
if (holdSpent || leaseSpent) {
|
|
38062
|
+
await journal.clearActive(eventTime(now));
|
|
38063
|
+
after = null;
|
|
38064
|
+
options.onEvent?.({
|
|
38065
|
+
type: "delivery_hold_released",
|
|
38066
|
+
signalId: signal.id,
|
|
38067
|
+
reason: holdSpent ? "hold_budget" : "lease_budget",
|
|
38068
|
+
heldMs: Math.max(0, now() - holdStartedAtMs),
|
|
38069
|
+
ts: eventTime(now)
|
|
38070
|
+
});
|
|
37715
38071
|
break;
|
|
37716
38072
|
}
|
|
37717
38073
|
const processed = await engine.process(signal);
|
|
@@ -38210,6 +38566,10 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
|
38210
38566
|
"lastAckOutcome",
|
|
38211
38567
|
"consecutiveAckFailureCount",
|
|
38212
38568
|
"lastAckSignalId",
|
|
38569
|
+
"currentDeliverySignalId",
|
|
38570
|
+
"currentDeliverySince",
|
|
38571
|
+
"heldBackDeliveries",
|
|
38572
|
+
"pendingDeliveryCountAt",
|
|
38213
38573
|
"routeMode",
|
|
38214
38574
|
"deferOverChars",
|
|
38215
38575
|
"pendingForMainCount",
|
|
@@ -38254,6 +38614,30 @@ var STATUS_DELIVERY_KEYS = [
|
|
|
38254
38614
|
"consecutiveAckFailureCount"
|
|
38255
38615
|
];
|
|
38256
38616
|
var deliveryOutcomes = DELIVERY_ACK_OUTCOMES;
|
|
38617
|
+
var LISTENER_HELD_BACK_MAX = 16;
|
|
38618
|
+
function parseHeldBackDeliveries(value) {
|
|
38619
|
+
if (!Array.isArray(value) || value.length > LISTENER_HELD_BACK_MAX) return null;
|
|
38620
|
+
const parsed = [];
|
|
38621
|
+
for (const item of value) {
|
|
38622
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return null;
|
|
38623
|
+
const entry = item;
|
|
38624
|
+
for (const key2 of Object.keys(entry)) {
|
|
38625
|
+
if (key2 !== "signalId" && key2 !== "at" && key2 !== "reason") return null;
|
|
38626
|
+
}
|
|
38627
|
+
if (typeof entry.signalId !== "string" || !UUID_RE18.test(entry.signalId) || typeof entry.at !== "string" || !Number.isFinite(Date.parse(entry.at)) || typeof entry.reason !== "string" || !LISTENER_DELIVERY_HOLD_RELEASE_REASONS.includes(
|
|
38628
|
+
entry.reason
|
|
38629
|
+
)) {
|
|
38630
|
+
return null;
|
|
38631
|
+
}
|
|
38632
|
+
if (parsed.some((seen) => seen.signalId === entry.signalId)) return null;
|
|
38633
|
+
parsed.push({
|
|
38634
|
+
signalId: entry.signalId,
|
|
38635
|
+
at: entry.at,
|
|
38636
|
+
reason: entry.reason
|
|
38637
|
+
});
|
|
38638
|
+
}
|
|
38639
|
+
return parsed;
|
|
38640
|
+
}
|
|
38257
38641
|
function parseStatus(raw, rejectUnknownKeys = false) {
|
|
38258
38642
|
let value;
|
|
38259
38643
|
try {
|
|
@@ -38277,7 +38661,8 @@ function parseStatus(raw, rejectUnknownKeys = false) {
|
|
|
38277
38661
|
const nullableCount = (candidate) => candidate === null || typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate >= 0;
|
|
38278
38662
|
const nullableTimestamp3 = (candidate) => candidate === null || typeof candidate === "string" && Number.isFinite(Date.parse(candidate));
|
|
38279
38663
|
const readHealth = row.readHealth === void 0 ? void 0 : parseListenerReadHealth(row.readHealth, rejectUnknownKeys);
|
|
38280
|
-
|
|
38664
|
+
const heldBackDeliveries = row.heldBackDeliveries === void 0 ? void 0 : parseHeldBackDeliveries(row.heldBackDeliveries);
|
|
38665
|
+
if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE18.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE18.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE18.test(row.principalId) || !Number.isSafeInteger(row.pid) || row.pid < 1 || typeof row.state !== "string" || !["starting", "ready", "stopping", "stopped", "failed"].includes(row.state) || typeof row.startedAt !== "string" || !Number.isFinite(Date.parse(row.startedAt)) || !(row.readyAt === null || typeof row.readyAt === "string" && Number.isFinite(Date.parse(row.readyAt))) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt)) || !(row.stoppedAt === null || typeof row.stoppedAt === "string" && Number.isFinite(Date.parse(row.stoppedAt))) || !nullableUuid3(row.lastSignalId) || !(row.lastErrorCode === null || typeof row.lastErrorCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorCode)) || !(row.lastErrorDetail === void 0 || row.lastErrorDetail === null || typeof row.lastErrorDetail === "string" && row.lastErrorDetail.length > 0 && row.lastErrorDetail.length <= 2048 && !/swm_(?:agt|inv|cap)_/i.test(row.lastErrorDetail)) || !(row.lastErrorReasonCode === void 0 || row.lastErrorReasonCode === null || typeof row.lastErrorReasonCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorReasonCode)) || !(row.providerExecutable === void 0 || row.providerExecutable === null || typeof row.providerExecutable === "string" && (0, import_node_path16.isAbsolute)(row.providerExecutable)) || !(row.providerVersion === void 0 || row.providerVersion === null || typeof row.providerVersion === "string" && SEMVER_RE2.test(row.providerVersion)) || !(row.providerLastMeasuredVersion === void 0 || row.providerLastMeasuredVersion === null || typeof row.providerLastMeasuredVersion === "string" && SEMVER_RE2.test(row.providerLastMeasuredVersion)) || !(row.providerBundledAgentSdkVersion === void 0 || row.providerBundledAgentSdkVersion === null || typeof row.providerBundledAgentSdkVersion === "string" && SEMVER_RE2.test(row.providerBundledAgentSdkVersion)) || !(row.providerBundledClaudeCodeVersion === void 0 || row.providerBundledClaudeCodeVersion === null || typeof row.providerBundledClaudeCodeVersion === "string" && SEMVER_RE2.test(row.providerBundledClaudeCodeVersion)) || !(row.providerMinimumRequiredVersion === void 0 || row.providerMinimumRequiredVersion === null || typeof row.providerMinimumRequiredVersion === "string" && SEMVER_RE2.test(row.providerMinimumRequiredVersion)) || !(row.cswarmVersion === void 0 || row.cswarmVersion === null || typeof row.cswarmVersion === "string" && SEMVER_RE2.test(row.cswarmVersion)) || (row.providerVersion === null || row.providerVersion === void 0) !== (row.providerLastMeasuredVersion === null || row.providerLastMeasuredVersion === void 0) || !(row.lastWorkerStderrTail === void 0 || row.lastWorkerStderrTail === null || typeof row.lastWorkerStderrTail === "string" && row.lastWorkerStderrTail.length > 0 && row.lastWorkerStderrTail.length <= 2048 && !/swm_(?:agt|inv|cap)_/i.test(row.lastWorkerStderrTail)) || typeof row.logPath !== "string" || !(0, import_node_path16.isAbsolute)(row.logPath) || !(row.deliveryMode === void 0 || row.deliveryMode === null || typeof row.deliveryMode === "string" && STATUS_DELIVERY_MODES.has(row.deliveryMode)) || !(row.pendingDeliveryCount === void 0 || nullableCount(row.pendingDeliveryCount)) || !(row.lastTerminalDeliveryFailureCount === void 0 || nullableCount(row.lastTerminalDeliveryFailureCount)) || !(row.lastTerminalDeliveryFailureAt === void 0 || nullableTimestamp3(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp3(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp3(row.lastAckAt)) || !(row.lastAckOutcome === void 0 || row.lastAckOutcome === null || typeof row.lastAckOutcome === "string" && deliveryOutcomes.has(row.lastAckOutcome)) || !(row.consecutiveAckFailureCount === void 0 || nullableCount(row.consecutiveAckFailureCount)) || !(row.lastAckSignalId === void 0 || row.lastAckSignalId === null || typeof row.lastAckSignalId === "string" && UUID_RE18.test(row.lastAckSignalId)) || !(row.currentDeliverySignalId === void 0 || row.currentDeliverySignalId === null || typeof row.currentDeliverySignalId === "string" && UUID_RE18.test(row.currentDeliverySignalId)) || !(row.currentDeliverySince === void 0 || nullableTimestamp3(row.currentDeliverySince)) || heldBackDeliveries === null || !(row.pendingDeliveryCountAt === void 0 || nullableTimestamp3(row.pendingDeliveryCountAt)) || !(row.routeMode === void 0 || row.routeMode === "worker" || row.routeMode === "main" || row.routeMode === "split") || !(row.deferOverChars === void 0 || row.deferOverChars === null || typeof row.deferOverChars === "number" && Number.isSafeInteger(row.deferOverChars) && row.deferOverChars >= 1 && row.deferOverChars <= 1e4) || !(row.pendingForMainCount === void 0 || typeof row.pendingForMainCount === "number" && Number.isSafeInteger(row.pendingForMainCount) && row.pendingForMainCount >= 0) || !(row.droppedForMainCount === void 0 || typeof row.droppedForMainCount === "number" && Number.isSafeInteger(row.droppedForMainCount) && row.droppedForMainCount >= 0) || readHealth === null || !(row.connectionsOpened === void 0 || typeof row.connectionsOpened === "number" && Number.isSafeInteger(row.connectionsOpened) && row.connectionsOpened >= 0) || !(row.connectionReuseRatio === void 0 || typeof row.connectionReuseRatio === "number" && Number.isFinite(row.connectionReuseRatio) && row.connectionReuseRatio >= 0) || !(row.activityPublishFailures === void 0 || typeof row.activityPublishFailures === "number" && Number.isSafeInteger(row.activityPublishFailures) && row.activityPublishFailures >= 0) || !(row.activityLastErrorCode === void 0 || row.activityLastErrorCode === null || typeof row.activityLastErrorCode === "string" && STATUS_ACTIVITY_ERROR_CODES.has(
|
|
38281
38666
|
row.activityLastErrorCode
|
|
38282
38667
|
))) {
|
|
38283
38668
|
throw new Error("stored listener status is malformed");
|
|
@@ -38303,6 +38688,16 @@ function parseStatus(raw, rejectUnknownKeys = false) {
|
|
|
38303
38688
|
// Optional key: present only when the file carried it, so a status written
|
|
38304
38689
|
// without it round-trips byte-for-byte (the routeMode pattern).
|
|
38305
38690
|
...row.lastAckSignalId === void 0 ? {} : { lastAckSignalId: row.lastAckSignalId ?? null },
|
|
38691
|
+
...row.currentDeliverySignalId === void 0 ? {} : {
|
|
38692
|
+
currentDeliverySignalId: row.currentDeliverySignalId ?? null
|
|
38693
|
+
},
|
|
38694
|
+
...row.currentDeliverySince === void 0 ? {} : {
|
|
38695
|
+
currentDeliverySince: row.currentDeliverySince ?? null
|
|
38696
|
+
},
|
|
38697
|
+
...heldBackDeliveries === void 0 ? {} : { heldBackDeliveries },
|
|
38698
|
+
...row.pendingDeliveryCountAt === void 0 ? {} : {
|
|
38699
|
+
pendingDeliveryCountAt: row.pendingDeliveryCountAt ?? null
|
|
38700
|
+
},
|
|
38306
38701
|
lastErrorDetail: row.lastErrorDetail ?? null,
|
|
38307
38702
|
lastWorkerStderrTail: row.lastWorkerStderrTail ?? null,
|
|
38308
38703
|
providerVersion: row.providerVersion ?? null,
|
|
@@ -38379,7 +38774,10 @@ async function appendListenerEvent(paths, event) {
|
|
|
38379
38774
|
"defer_over_chars",
|
|
38380
38775
|
"body_length",
|
|
38381
38776
|
"pending_main_count",
|
|
38382
|
-
"dropped_count"
|
|
38777
|
+
"dropped_count",
|
|
38778
|
+
// How long one delivery held the worker seat, and why it gave it back.
|
|
38779
|
+
"held_ms",
|
|
38780
|
+
"release_reason"
|
|
38383
38781
|
]);
|
|
38384
38782
|
const deliveryModes = /* @__PURE__ */ new Set(["durable_claim", "cursor_fallback"]);
|
|
38385
38783
|
const routeModes = /* @__PURE__ */ new Set(["worker", "main", "split"]);
|
|
@@ -38441,6 +38839,14 @@ async function appendListenerEvent(paths, event) {
|
|
|
38441
38839
|
if ((key2 === "body_length" || key2 === "pending_main_count" || key2 === "dropped_count") && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
|
|
38442
38840
|
throw new Error("listener event main-route count is not allowed");
|
|
38443
38841
|
}
|
|
38842
|
+
if (key2 === "held_ms" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
|
|
38843
|
+
throw new Error("listener event hold duration is not allowed");
|
|
38844
|
+
}
|
|
38845
|
+
if (key2 === "release_reason" && !(typeof value === "string" && LISTENER_DELIVERY_HOLD_RELEASE_REASONS.includes(
|
|
38846
|
+
value
|
|
38847
|
+
))) {
|
|
38848
|
+
throw new Error("listener event hold release reason is not allowed");
|
|
38849
|
+
}
|
|
38444
38850
|
if (key2 === "worker_stderr_tail" && !(typeof value === "string" && value.length > 0 && value.length <= 2048)) {
|
|
38445
38851
|
throw new Error("listener event stderr tail is not allowed");
|
|
38446
38852
|
}
|
|
@@ -38820,6 +39226,7 @@ async function runListenerSupervisor(options) {
|
|
|
38820
39226
|
lastWorkerStderrTail: null,
|
|
38821
39227
|
deliveryMode: null,
|
|
38822
39228
|
pendingDeliveryCount: null,
|
|
39229
|
+
pendingDeliveryCountAt: null,
|
|
38823
39230
|
lastTerminalDeliveryFailureCount: null,
|
|
38824
39231
|
lastTerminalDeliveryFailureAt: null,
|
|
38825
39232
|
lastClaimAt: null,
|
|
@@ -38831,6 +39238,11 @@ async function runListenerSupervisor(options) {
|
|
|
38831
39238
|
lastAckOutcome: carried?.lastAckOutcome ?? null,
|
|
38832
39239
|
consecutiveAckFailureCount: carried?.consecutiveAckFailureCount ?? null,
|
|
38833
39240
|
lastAckSignalId: carried?.lastAckSignalId ?? null,
|
|
39241
|
+
/* Never carried across a restart: a seat this process does not hold cannot
|
|
39242
|
+
be reported as held, and the queue age restarts with the observations. */
|
|
39243
|
+
currentDeliverySignalId: null,
|
|
39244
|
+
currentDeliverySince: null,
|
|
39245
|
+
heldBackDeliveries: [],
|
|
38834
39246
|
routeMode: options.routeMode ?? "worker",
|
|
38835
39247
|
deferOverChars: options.deferOverChars ?? null,
|
|
38836
39248
|
pendingForMainCount: 0,
|
|
@@ -38864,9 +39276,15 @@ async function runListenerSupervisor(options) {
|
|
|
38864
39276
|
chain(() => appendListenerEvent(options.paths, event));
|
|
38865
39277
|
};
|
|
38866
39278
|
const transition = (state, changes = {}) => {
|
|
39279
|
+
const notWatching = state === "starting" || state === "stopped" || state === "failed";
|
|
38867
39280
|
status = {
|
|
38868
39281
|
...status,
|
|
38869
39282
|
...changes,
|
|
39283
|
+
...notWatching ? {
|
|
39284
|
+
currentDeliverySignalId: null,
|
|
39285
|
+
currentDeliverySince: null,
|
|
39286
|
+
heldBackDeliveries: []
|
|
39287
|
+
} : {},
|
|
38870
39288
|
state,
|
|
38871
39289
|
updatedAt: iso2(now)
|
|
38872
39290
|
};
|
|
@@ -39055,6 +39473,7 @@ async function runListenerSupervisor(options) {
|
|
|
39055
39473
|
...status,
|
|
39056
39474
|
deliveryMode: event.mode,
|
|
39057
39475
|
pendingDeliveryCount: event.pendingDeliveryCount,
|
|
39476
|
+
pendingDeliveryCountAt: event.pendingDeliveryCount === null ? null : event.ts,
|
|
39058
39477
|
updatedAt: event.ts
|
|
39059
39478
|
};
|
|
39060
39479
|
persist();
|
|
@@ -39067,6 +39486,7 @@ async function runListenerSupervisor(options) {
|
|
|
39067
39486
|
return;
|
|
39068
39487
|
}
|
|
39069
39488
|
if (event.type === "delivery_claim") {
|
|
39489
|
+
const heldBack = (status.heldBackDeliveries ?? []).filter((entry) => entry.signalId !== event.signalId);
|
|
39070
39490
|
status = {
|
|
39071
39491
|
...status,
|
|
39072
39492
|
readHealth: recordListenerClaim(
|
|
@@ -39074,6 +39494,10 @@ async function runListenerSupervisor(options) {
|
|
|
39074
39494
|
event.ts
|
|
39075
39495
|
),
|
|
39076
39496
|
pendingDeliveryCount: event.pendingDeliveryCount,
|
|
39497
|
+
pendingDeliveryCountAt: event.ts,
|
|
39498
|
+
currentDeliverySignalId: event.signalId,
|
|
39499
|
+
currentDeliverySince: event.signalId === null ? null : event.ts,
|
|
39500
|
+
heldBackDeliveries: heldBack,
|
|
39077
39501
|
lastClaimAt: event.ts,
|
|
39078
39502
|
updatedAt: event.ts
|
|
39079
39503
|
};
|
|
@@ -39105,6 +39529,36 @@ async function runListenerSupervisor(options) {
|
|
|
39105
39529
|
});
|
|
39106
39530
|
return;
|
|
39107
39531
|
}
|
|
39532
|
+
if (event.type === "delivery_hold_released") {
|
|
39533
|
+
status = {
|
|
39534
|
+
...status,
|
|
39535
|
+
currentDeliverySignalId: null,
|
|
39536
|
+
currentDeliverySince: null,
|
|
39537
|
+
/* Held back, NOT waiting to be claimed: the row keeps its live lease,
|
|
39538
|
+
so the service cannot hand it to anyone until that lease expires.
|
|
39539
|
+
Both review arms on 33cd24b measured the earlier wording counting it
|
|
39540
|
+
among deliveries "waiting to be claimed". Newest first, deduplicated
|
|
39541
|
+
on the id (a row can be released, redelivered and released again),
|
|
39542
|
+
and bounded. */
|
|
39543
|
+
heldBackDeliveries: [
|
|
39544
|
+
{ signalId: event.signalId, at: event.ts, reason: event.reason },
|
|
39545
|
+
...(status.heldBackDeliveries ?? []).filter(
|
|
39546
|
+
(entry) => entry.signalId !== event.signalId
|
|
39547
|
+
)
|
|
39548
|
+
].slice(0, LISTENER_HELD_BACK_MAX),
|
|
39549
|
+
lastSignalId: event.signalId,
|
|
39550
|
+
updatedAt: event.ts
|
|
39551
|
+
};
|
|
39552
|
+
persist();
|
|
39553
|
+
log({
|
|
39554
|
+
ts: event.ts,
|
|
39555
|
+
event: "listener_delivery_hold_released",
|
|
39556
|
+
signal_id: event.signalId,
|
|
39557
|
+
release_reason: event.reason,
|
|
39558
|
+
held_ms: Math.max(0, Math.trunc(event.heldMs))
|
|
39559
|
+
});
|
|
39560
|
+
return;
|
|
39561
|
+
}
|
|
39108
39562
|
if (event.type === "delivery_ack") {
|
|
39109
39563
|
const failed = event.outcome === "failed_terminal";
|
|
39110
39564
|
const providerProven = DELIVERY_PROVIDER_PROVEN_OUTCOMES.has(event.outcome);
|
|
@@ -39115,6 +39569,13 @@ async function runListenerSupervisor(options) {
|
|
|
39115
39569
|
lastAckSignalId: event.signalId,
|
|
39116
39570
|
consecutiveAckFailureCount: failed ? (status.consecutiveAckFailureCount ?? 0) + 1 : providerProven ? 0 : status.consecutiveAckFailureCount,
|
|
39117
39571
|
pendingDeliveryCount: null,
|
|
39572
|
+
pendingDeliveryCountAt: null,
|
|
39573
|
+
currentDeliverySignalId: null,
|
|
39574
|
+
currentDeliverySince: null,
|
|
39575
|
+
// An acknowledged row is answered and gone; drop just that one.
|
|
39576
|
+
heldBackDeliveries: (status.heldBackDeliveries ?? []).filter(
|
|
39577
|
+
(entry) => entry.signalId !== event.signalId
|
|
39578
|
+
),
|
|
39118
39579
|
lastSignalId: event.signalId,
|
|
39119
39580
|
updatedAt: event.ts
|
|
39120
39581
|
};
|
|
@@ -39285,7 +39746,14 @@ async function effectiveListenerStatus(paths) {
|
|
|
39285
39746
|
state: "failed",
|
|
39286
39747
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
39287
39748
|
stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
39288
|
-
lastErrorCode: "unclean_exit"
|
|
39749
|
+
lastErrorCode: "unclean_exit",
|
|
39750
|
+
/* The process is gone: it holds nothing and observes nothing, so every
|
|
39751
|
+
field whose sentence is rendered in the present tense against read
|
|
39752
|
+
time is cleared. pendingDeliveryCount stays, because its line already
|
|
39753
|
+
says it is what the service reported. */
|
|
39754
|
+
currentDeliverySignalId: null,
|
|
39755
|
+
currentDeliverySince: null,
|
|
39756
|
+
heldBackDeliveries: []
|
|
39289
39757
|
};
|
|
39290
39758
|
await writeListenerStatus(paths, failed);
|
|
39291
39759
|
return failed;
|
|
@@ -42069,8 +42537,11 @@ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
42069
42537
|
"foreground",
|
|
42070
42538
|
"grok-executable",
|
|
42071
42539
|
"head-sha",
|
|
42540
|
+
"broadcast-to-channel",
|
|
42541
|
+
"channel",
|
|
42072
42542
|
"help",
|
|
42073
42543
|
"if-version",
|
|
42544
|
+
"include-archived",
|
|
42074
42545
|
"include-stale",
|
|
42075
42546
|
"include-tombstoned",
|
|
42076
42547
|
"invitation-id",
|
|
@@ -42090,6 +42561,7 @@ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
42090
42561
|
"permissions",
|
|
42091
42562
|
"principal-id",
|
|
42092
42563
|
"provider",
|
|
42564
|
+
"purpose",
|
|
42093
42565
|
"renewal-grant-id",
|
|
42094
42566
|
"repo",
|
|
42095
42567
|
"reveal-anon-key",
|
|
@@ -42099,6 +42571,7 @@ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
42099
42571
|
"site",
|
|
42100
42572
|
"slug",
|
|
42101
42573
|
"state-dir",
|
|
42574
|
+
"thread",
|
|
42102
42575
|
"renewal-horizon-days",
|
|
42103
42576
|
"standing",
|
|
42104
42577
|
"task-id",
|
|
@@ -42119,12 +42592,14 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
42119
42592
|
"agent-token-stdin",
|
|
42120
42593
|
"all-devices",
|
|
42121
42594
|
"allow-unattended",
|
|
42595
|
+
"broadcast-to-channel",
|
|
42122
42596
|
"confirm-standing",
|
|
42123
42597
|
"force-file-store",
|
|
42124
42598
|
"follow",
|
|
42125
42599
|
"force",
|
|
42126
42600
|
"foreground",
|
|
42127
42601
|
"help",
|
|
42602
|
+
"include-archived",
|
|
42128
42603
|
"include-stale",
|
|
42129
42604
|
"include-tombstoned",
|
|
42130
42605
|
"invitation-token-stdin",
|
|
@@ -42137,13 +42612,14 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
42137
42612
|
"reveal-anon-key",
|
|
42138
42613
|
"repo",
|
|
42139
42614
|
"standing",
|
|
42615
|
+
"thread",
|
|
42140
42616
|
"user",
|
|
42141
42617
|
"write"
|
|
42142
42618
|
]);
|
|
42143
42619
|
var UUID_RE23 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
42144
42620
|
function packageVersion() {
|
|
42145
|
-
if ("0.1.
|
|
42146
|
-
return "0.1.
|
|
42621
|
+
if ("0.1.55".length > 0) {
|
|
42622
|
+
return "0.1.55";
|
|
42147
42623
|
}
|
|
42148
42624
|
try {
|
|
42149
42625
|
const value = JSON.parse(
|
|
@@ -42262,15 +42738,19 @@ Usage:
|
|
|
42262
42738
|
cswarm whoami ${requiredAgentCredential} [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--json]
|
|
42263
42739
|
cswarm resume --agent-token-file <path> [--url <url> --anon-key <key>] --workspace-id <uuid> [--json]
|
|
42264
42740
|
cswarm members [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
42265
|
-
cswarm working-on "<what>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--about <ref>] [--until <dur>] [--json]
|
|
42266
|
-
cswarm note "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--to <member|agent>] [--about <ref>] [--attach <path> ...] [--until <dur>] [--json] # text: 1..8000 characters
|
|
42267
|
-
cswarm ask "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--to <member|agent>] [--about <ref>] [--attach <path> ...] [--until <dur>] [--wait <seconds>] [--json] # text: 1..8000 characters
|
|
42268
|
-
cswarm reply <signal-id> "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--attach <path> ...] [--until <dur>] [--json]
|
|
42741
|
+
cswarm working-on "<what>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--about <ref>] [--channel <name>] [--until <dur>] [--json]
|
|
42742
|
+
cswarm note "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--to <member|agent>] [--about <ref>] [--channel <name>] [--attach <path> ...] [--until <dur>] [--json] # text: 1..8000 characters
|
|
42743
|
+
cswarm ask "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--to <member|agent>] [--about <ref>] [--channel <name>] [--attach <path> ...] [--until <dur>] [--wait <seconds>] [--json] # text: 1..8000 characters
|
|
42744
|
+
cswarm reply <signal-id> "<text>" [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--thread [--broadcast-to-channel]] [--attach <path> ...] [--until <dur>] [--json]
|
|
42269
42745
|
cswarm receipt <signal-id> ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> [--json]
|
|
42270
|
-
cswarm feed [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--about <ref>] [--kind <kind>] [--since <timestamp>] [--limit <n>] [--include-stale] [--json]
|
|
42271
|
-
cswarm inbox [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--kind <kind>] [--about <ref>] [--since <timestamp>] [--limit <n>] [--include-stale] [--wait <seconds>] [--json]
|
|
42746
|
+
cswarm feed [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--about <ref>] [--kind <kind>] [--channel <name>] [--since <timestamp>] [--limit <n>] [--include-stale] [--json]
|
|
42747
|
+
cswarm inbox [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--kind <kind>] [--about <ref>] [--channel <name>] [--since <timestamp>] [--limit <n>] [--include-stale] [--wait <seconds>] [--json]
|
|
42272
42748
|
cswarm inbox --notify ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> [--json]
|
|
42273
42749
|
cswarm inbox --follow --ndjson [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--kind <kind>] [--about <ref>] [--since <timestamp>] [--limit <n>] [--include-stale]
|
|
42750
|
+
cswarm channel create <name> [--purpose <text>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json] # purpose: at most ${CHANNEL_PURPOSE_MAX} characters
|
|
42751
|
+
cswarm channel ls [--include-archived] [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--json]
|
|
42752
|
+
cswarm channel rename <name|channel-id> <new-name> [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
42753
|
+
cswarm channel archive <name|channel-id> [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
42274
42754
|
cswarm file put <local-path> [--name <name>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
42275
42755
|
cswarm file ls [--include-tombstoned] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
42276
42756
|
cswarm file get <name|file-id> [--version <n>] [--out <local-path>] [--force] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
@@ -42327,6 +42807,11 @@ Credential selection for command/dogfood:
|
|
|
42327
42807
|
signal command/read only -- either form
|
|
42328
42808
|
receipt reads only -- either form
|
|
42329
42809
|
inbox --notify persists a per-agent cursor -- needs principal_id
|
|
42810
|
+
channel create, channel rename, channel archive
|
|
42811
|
+
command only, nothing persisted -- either form
|
|
42812
|
+
channel ls reads swarm_read.channels over REST -- signed-in
|
|
42813
|
+
person only; the read service has no channels
|
|
42814
|
+
resource for an agent credential
|
|
42330
42815
|
file put, file ls, file get, file rm, file restore,
|
|
42331
42816
|
brain ls, brain get, brain put
|
|
42332
42817
|
read and command, nothing persisted -- either form
|
|
@@ -42352,6 +42837,14 @@ Credential selection for command/dogfood:
|
|
|
42352
42837
|
Found a bug or missing feature in cswarm itself? cswarm feedback sends it to the
|
|
42353
42838
|
deployment's operators \u2014 agents are encouraged to report friction they hit.
|
|
42354
42839
|
|
|
42840
|
+
A channel is where a message is FILED, not who may read it. Everyone in the
|
|
42841
|
+
workspace reads every channel, and --channel changes nothing about who sees a
|
|
42842
|
+
signal. Archiving a channel keeps its history and its permalinks and refuses new
|
|
42843
|
+
messages. cswarm reply --thread answers in the open, in the thread of the signal
|
|
42844
|
+
you name, so it takes no recipient; add --broadcast-to-channel to send that reply
|
|
42845
|
+
to the thread's channel as well. Plain cswarm reply is unchanged and still
|
|
42846
|
+
answers the original author privately.
|
|
42847
|
+
|
|
42355
42848
|
Signals (intention sharing) accept the same credential selection. Agent mode
|
|
42356
42849
|
never opens a browser or infers a human's saved workspace. Durations use a whole
|
|
42357
42850
|
number plus m, h, or d (for example 90m, 24h, or 7d) and are capped at 30d.
|
|
@@ -42368,7 +42861,10 @@ due \u2014 a turn never outlives its credential. Right after a rotation the full
|
|
|
42368
42861
|
budget is available up to the token TTL minus 60s (about 59m on the default 1h
|
|
42369
42862
|
TTL); a turn that lands just before a rotation can be clamped to the ~5m
|
|
42370
42863
|
renewal lead, and if it times out there, durable delivery retries it on the
|
|
42371
|
-
fresh credential.
|
|
42864
|
+
fresh credential. The same budget also bounds how long ONE delivery may hold the
|
|
42865
|
+
worker seat across its retries: when it is spent the listener hands the seat
|
|
42866
|
+
back and claims the next delivery. After the lease ends the service either
|
|
42867
|
+
delivers the released one again or terminates it.
|
|
42372
42868
|
|
|
42373
42869
|
listen start --route worker|main|split chooses where directed messages go. worker
|
|
42374
42870
|
is the unchanged default. main queues every ask or note for the interactive session.
|
|
@@ -43859,6 +44355,19 @@ function signalKind(value) {
|
|
|
43859
44355
|
}
|
|
43860
44356
|
return value;
|
|
43861
44357
|
}
|
|
44358
|
+
function channelOption(args) {
|
|
44359
|
+
const value = args.optional("channel");
|
|
44360
|
+
if (value === void 0) return void 0;
|
|
44361
|
+
const problem = channelSlugProblem(value);
|
|
44362
|
+
if (problem !== null) throw new Error(problem);
|
|
44363
|
+
return normalizeChannelSlug(value);
|
|
44364
|
+
}
|
|
44365
|
+
function unknownChannelReadMessage(error, slug) {
|
|
44366
|
+
const details = followHttpDetails(error);
|
|
44367
|
+
if (details === null || details.status !== 404) return null;
|
|
44368
|
+
if (followErrorEnvelope(error).error !== "channel_not_found") return null;
|
|
44369
|
+
return `There is no channel named ${slug} in this workspace. Nothing was read. Create it with cswarm channel create ${slug}, or drop --channel to read everything.`;
|
|
44370
|
+
}
|
|
43862
44371
|
function signalDuration(value) {
|
|
43863
44372
|
if (value === void 0) return void 0;
|
|
43864
44373
|
const match = /^([1-9]\d*)(m|h|d)$/.exec(value);
|
|
@@ -44160,11 +44669,13 @@ async function runPostSignal(args, kind) {
|
|
|
44160
44669
|
...CREDENTIAL_FLAGS,
|
|
44161
44670
|
...allowTo ? ["to"] : [],
|
|
44162
44671
|
"about",
|
|
44672
|
+
"channel",
|
|
44163
44673
|
"until",
|
|
44164
44674
|
...allowWait ? ["wait"] : [],
|
|
44165
44675
|
...allowTo ? ["attach"] : [],
|
|
44166
44676
|
"json"
|
|
44167
44677
|
], 2);
|
|
44678
|
+
const channel = channelOption(args);
|
|
44168
44679
|
const preparedAttachments = allowTo ? prepareSignalAttachments(args.all("attach")) : [];
|
|
44169
44680
|
const waitSeconds = allowWait && args.optional("wait") !== void 0 ? parseWaitSeconds(args.required("wait")) : void 0;
|
|
44170
44681
|
const cloud = await target(args);
|
|
@@ -44209,7 +44720,8 @@ async function runPostSignal(args, kind) {
|
|
|
44209
44720
|
...postSignalTargets(recipient),
|
|
44210
44721
|
about: args.optional("about") === void 0 ? null : signalText(args.required("about"), "about"),
|
|
44211
44722
|
...attachments.length === 0 ? {} : { attachments },
|
|
44212
|
-
...untilMs2 === void 0 ? {} : { until_ms: untilMs2 }
|
|
44723
|
+
...untilMs2 === void 0 ? {} : { until_ms: untilMs2 },
|
|
44724
|
+
...channel === void 0 ? {} : { channel }
|
|
44213
44725
|
};
|
|
44214
44726
|
let result;
|
|
44215
44727
|
try {
|
|
@@ -44334,15 +44846,35 @@ function replyRefusalHint(error) {
|
|
|
44334
44846
|
if (!(error instanceof CommandHttpError) || error.status !== 403) return null;
|
|
44335
44847
|
return "reply was refused (403). The most common cause is that the signal was not addressed to you \u2014 you cannot reply to your own ask; reply to the other party's signal, reach someone directly with cswarm ask --to <agent>, or post a channel-visible cswarm note. If you did receive that signal, the refusal is an authorization one instead: the credential may be revoked or expired, or it may not be a member of this workspace.";
|
|
44336
44848
|
}
|
|
44849
|
+
function threadReplyMessage(signal, options) {
|
|
44850
|
+
if (!options.inThread) {
|
|
44851
|
+
return "Reply shared. It is immutable and addressed to the original author.";
|
|
44852
|
+
}
|
|
44853
|
+
const inThread = "Reply shared in the thread. It is immutable and readable by everyone who can read the thread.";
|
|
44854
|
+
if (!options.broadcastToChannel) return inThread;
|
|
44855
|
+
if (signal.channel_id === void 0) {
|
|
44856
|
+
return `${inThread} This deployment did not say which channel the thread is in, so whether it also reached a channel is unknown.`;
|
|
44857
|
+
}
|
|
44858
|
+
return signal.channel_id === null ? `${inThread} Its thread is in no channel, so --broadcast-to-channel had nothing to send it to.` : "Reply shared in the thread and sent to the thread's channel as well. It is immutable and readable by everyone who can read the thread.";
|
|
44859
|
+
}
|
|
44337
44860
|
async function runReply(args) {
|
|
44338
44861
|
args.assertShape([
|
|
44339
44862
|
...TARGET_FLAGS,
|
|
44340
44863
|
"workspace-id",
|
|
44341
44864
|
...CREDENTIAL_FLAGS,
|
|
44342
44865
|
"attach",
|
|
44866
|
+
"broadcast-to-channel",
|
|
44867
|
+
"thread",
|
|
44343
44868
|
"until",
|
|
44344
44869
|
"json"
|
|
44345
44870
|
], 3);
|
|
44871
|
+
const inThread = args.has("thread");
|
|
44872
|
+
const broadcastToChannel = args.has("broadcast-to-channel");
|
|
44873
|
+
if (broadcastToChannel && !inThread) {
|
|
44874
|
+
throw new UsageError(
|
|
44875
|
+
"--broadcast-to-channel sends a thread reply to its channel as well, so it needs --thread"
|
|
44876
|
+
);
|
|
44877
|
+
}
|
|
44346
44878
|
const signalId = args.positionals[1];
|
|
44347
44879
|
if (signalId === void 0 || !UUID_RE23.test(signalId)) {
|
|
44348
44880
|
throw new Error("reply requires the signal UUID being answered");
|
|
@@ -44368,24 +44900,30 @@ async function runReply(args) {
|
|
|
44368
44900
|
body: signalText(body, "body"),
|
|
44369
44901
|
to_user_id: null,
|
|
44370
44902
|
to_agent_principal_id: null,
|
|
44371
|
-
in_reply_to: signalId.toLowerCase(),
|
|
44903
|
+
in_reply_to: inThread ? null : signalId.toLowerCase(),
|
|
44372
44904
|
about: null,
|
|
44373
44905
|
...attachments.length === 0 ? {} : { attachments },
|
|
44374
|
-
...untilMs2 === void 0 ? {} : { until_ms: untilMs2 }
|
|
44906
|
+
...untilMs2 === void 0 ? {} : { until_ms: untilMs2 },
|
|
44907
|
+
...inThread ? { thread_root_id: signalId.toLowerCase() } : {},
|
|
44908
|
+
...broadcastToChannel ? { broadcast_to_channel: true } : {}
|
|
44375
44909
|
};
|
|
44376
44910
|
let result;
|
|
44377
44911
|
try {
|
|
44378
44912
|
result = await postSignalCommand(cloud, credential, command2);
|
|
44379
44913
|
} catch (error) {
|
|
44380
|
-
const hint = replyRefusalHint(error);
|
|
44914
|
+
const hint = inThread ? null : replyRefusalHint(error);
|
|
44381
44915
|
if (hint !== null) throw new Error(hint);
|
|
44382
44916
|
throw error;
|
|
44383
44917
|
}
|
|
44384
44918
|
const signal = result.response.signal;
|
|
44919
|
+
const replyMessage = threadReplyMessage(signal, {
|
|
44920
|
+
inThread,
|
|
44921
|
+
broadcastToChannel
|
|
44922
|
+
});
|
|
44385
44923
|
if (args.has("json")) {
|
|
44386
44924
|
printJson({
|
|
44387
44925
|
status: result.response.status,
|
|
44388
|
-
message: "Reply shared. It is immutable, tenancy-scoped, and will quietly expire at its horizon.",
|
|
44926
|
+
message: inThread ? replyMessage : "Reply shared. It is immutable, tenancy-scoped, and will quietly expire at its horizon.",
|
|
44389
44927
|
signal,
|
|
44390
44928
|
retried: result.retried,
|
|
44391
44929
|
attempts: result.attempts
|
|
@@ -44400,7 +44938,7 @@ async function runReply(args) {
|
|
|
44400
44938
|
)
|
|
44401
44939
|
);
|
|
44402
44940
|
process.stdout.write(
|
|
44403
|
-
|
|
44941
|
+
`${replyMessage}
|
|
44404
44942
|
${renderSignals([signal], {
|
|
44405
44943
|
inbox: false,
|
|
44406
44944
|
includeStale: true,
|
|
@@ -44682,6 +45220,7 @@ async function runSignalRead(args, inbox) {
|
|
|
44682
45220
|
"workspace-id",
|
|
44683
45221
|
...CREDENTIAL_FLAGS,
|
|
44684
45222
|
"about",
|
|
45223
|
+
"channel",
|
|
44685
45224
|
"kind",
|
|
44686
45225
|
...inbox ? ["wait", "follow", "ndjson", "notify"] : [],
|
|
44687
45226
|
"since",
|
|
@@ -44697,6 +45236,9 @@ async function runSignalRead(args, inbox) {
|
|
|
44697
45236
|
if (!args.has("ndjson")) {
|
|
44698
45237
|
throw new Error("inbox --follow requires --ndjson");
|
|
44699
45238
|
}
|
|
45239
|
+
if (args.has("channel")) {
|
|
45240
|
+
throw new Error("inbox --follow cannot be combined with --channel");
|
|
45241
|
+
}
|
|
44700
45242
|
if (args.optional("wait") !== void 0) {
|
|
44701
45243
|
throw new Error("inbox --follow cannot be combined with --wait");
|
|
44702
45244
|
}
|
|
@@ -44709,15 +45251,29 @@ async function runSignalRead(args, inbox) {
|
|
|
44709
45251
|
if (inbox && args.has("ndjson")) {
|
|
44710
45252
|
throw new Error("inbox --ndjson requires --follow");
|
|
44711
45253
|
}
|
|
45254
|
+
const channelSlug = channelOption(args);
|
|
44712
45255
|
const waitSeconds = inbox && args.optional("wait") !== void 0 ? parseWaitSeconds(args.required("wait")) : void 0;
|
|
44713
45256
|
const cloud = await target(args);
|
|
44714
45257
|
const selected = await commandWorkspaceAndCredential(args, cloud, {
|
|
44715
45258
|
validateHumanWorkspace: true
|
|
44716
45259
|
});
|
|
44717
45260
|
const credential = signalCredentialOf(selected);
|
|
45261
|
+
let channelId;
|
|
45262
|
+
if (channelSlug !== void 0 && selected.kind === "human") {
|
|
45263
|
+
const rows4 = await listChannelsAsHuman(
|
|
45264
|
+
cloud,
|
|
45265
|
+
selected.human.accessToken,
|
|
45266
|
+
selected.selectedWorkspace
|
|
45267
|
+
);
|
|
45268
|
+
const match = findChannelBySlug(rows4, channelSlug);
|
|
45269
|
+
if (match === null) throw new Error(unknownChannelMessage(channelSlug, rows4));
|
|
45270
|
+
channelId = match.channel_id;
|
|
45271
|
+
}
|
|
44718
45272
|
const queryBase = {
|
|
44719
45273
|
workspaceId: selected.selectedWorkspace,
|
|
44720
45274
|
inbox,
|
|
45275
|
+
...channelSlug === void 0 || selected.kind !== "agent" ? {} : { channel: channelSlug },
|
|
45276
|
+
...channelId === void 0 ? {} : { channelId },
|
|
44721
45277
|
...args.optional("about") === void 0 ? {} : { about: signalText(args.required("about"), "about") },
|
|
44722
45278
|
...args.optional("kind") === void 0 ? {} : { kind: signalKind(args.required("kind")) },
|
|
44723
45279
|
...args.optional("since") === void 0 ? {} : { since: args.required("since") },
|
|
@@ -44727,17 +45283,23 @@ async function runSignalRead(args, inbox) {
|
|
|
44727
45283
|
let rows3;
|
|
44728
45284
|
let timedOut = false;
|
|
44729
45285
|
let waited = false;
|
|
44730
|
-
|
|
44731
|
-
|
|
44732
|
-
|
|
44733
|
-
|
|
44734
|
-
|
|
44735
|
-
|
|
44736
|
-
|
|
44737
|
-
|
|
44738
|
-
|
|
44739
|
-
|
|
44740
|
-
|
|
45286
|
+
try {
|
|
45287
|
+
if (waitSeconds === void 0) {
|
|
45288
|
+
rows3 = await readSignals(cloud, credential, queryBase);
|
|
45289
|
+
} else {
|
|
45290
|
+
waited = true;
|
|
45291
|
+
const deadlineMs = waitDeadlineMs(waitSeconds);
|
|
45292
|
+
const waitResult = await pollForSignals({
|
|
45293
|
+
deadlineMs,
|
|
45294
|
+
read: () => readSignals(cloud, credential, queryBase, { deadlineMs })
|
|
45295
|
+
});
|
|
45296
|
+
rows3 = waitResult.signals;
|
|
45297
|
+
timedOut = waitResult.timedOut;
|
|
45298
|
+
}
|
|
45299
|
+
} catch (error) {
|
|
45300
|
+
const named = channelSlug === void 0 ? null : unknownChannelReadMessage(error, channelSlug);
|
|
45301
|
+
if (named !== null) throw new Error(named);
|
|
45302
|
+
throw error;
|
|
44741
45303
|
}
|
|
44742
45304
|
if (args.has("json")) {
|
|
44743
45305
|
printJson(
|
|
@@ -44770,6 +45332,12 @@ async function runSignalRead(args, inbox) {
|
|
|
44770
45332
|
);
|
|
44771
45333
|
return;
|
|
44772
45334
|
}
|
|
45335
|
+
if (channelSlug !== void 0) {
|
|
45336
|
+
process.stdout.write(
|
|
45337
|
+
`${inbox ? "Inbox" : "Feed"}, filed in ${channelSlug}:
|
|
45338
|
+
`
|
|
45339
|
+
);
|
|
45340
|
+
}
|
|
44773
45341
|
process.stdout.write(`${renderSignals(rows3, {
|
|
44774
45342
|
inbox,
|
|
44775
45343
|
includeStale: args.has("include-stale"),
|
|
@@ -45391,6 +45959,12 @@ function listenerStatusJson(status, permissionMode, evidence = {
|
|
|
45391
45959
|
lastAckOutcome: status.lastAckOutcome ?? null,
|
|
45392
45960
|
consecutiveAckFailureCount: status.consecutiveAckFailureCount ?? null,
|
|
45393
45961
|
lastAckSignalId: status.lastAckSignalId ?? null,
|
|
45962
|
+
currentDeliverySignalId: status.currentDeliverySignalId ?? null,
|
|
45963
|
+
currentDeliverySince: status.currentDeliverySince ?? null,
|
|
45964
|
+
currentDeliveryElapsedMs: status.currentDeliverySince ? Math.max(0, nowMs - Date.parse(status.currentDeliverySince)) : null,
|
|
45965
|
+
pendingDeliveryCountAt: status.pendingDeliveryCountAt ?? null,
|
|
45966
|
+
heldBackDeliveries: status.heldBackDeliveries ?? [],
|
|
45967
|
+
heldBackDeliveryCount: (status.heldBackDeliveries ?? []).length,
|
|
45394
45968
|
routeMode: status.routeMode ?? "worker",
|
|
45395
45969
|
deferOverChars: status.deferOverChars ?? null,
|
|
45396
45970
|
pendingForMainCount: status.pendingForMainCount ?? 0,
|
|
@@ -45513,8 +46087,26 @@ function renderListenerStatus(status, evidence = {
|
|
|
45513
46087
|
lines.push("Delivery mode has not been reported yet.");
|
|
45514
46088
|
}
|
|
45515
46089
|
if (status.pendingDeliveryCount !== null) {
|
|
46090
|
+
const observedAt = status.pendingDeliveryCountAt ?? null;
|
|
46091
|
+
lines.push(
|
|
46092
|
+
`Pending deliveries reported by the service: ${status.pendingDeliveryCount}.` + (observedAt === null ? " When the service reported it was not recorded." : ` The service reported that ${relativeAge(observedAt, nowMs)}.`)
|
|
46093
|
+
);
|
|
46094
|
+
}
|
|
46095
|
+
const currentDeliveryId = status.currentDeliverySignalId ?? null;
|
|
46096
|
+
const currentDeliverySince = status.currentDeliverySince ?? null;
|
|
46097
|
+
if (currentDeliveryId !== null && currentDeliverySince !== null) {
|
|
45516
46098
|
lines.push(
|
|
45517
|
-
`
|
|
46099
|
+
`Working on delivery ${currentDeliveryId}, claimed ${relativeAge(currentDeliverySince, nowMs)}.`
|
|
46100
|
+
);
|
|
46101
|
+
} else {
|
|
46102
|
+
lines.push("No delivery is being worked on right now.");
|
|
46103
|
+
}
|
|
46104
|
+
const heldBack = status.heldBackDeliveries ?? [];
|
|
46105
|
+
const newestHeldBack = heldBack[0];
|
|
46106
|
+
if (newestHeldBack !== void 0) {
|
|
46107
|
+
const others = heldBack.length - 1;
|
|
46108
|
+
lines.push(
|
|
46109
|
+
`Delivery ${newestHeldBack.signalId} was handed back ${relativeAge(newestHeldBack.at, nowMs)} because ${LISTENER_DELIVERY_HOLD_RELEASE_CLAUSES[newestHeldBack.reason]}.` + (others > 0 ? ` This listener is still tracking ${others} other handed-back ${others === 1 ? "delivery" : "deliveries"}.` : "") + ` This listener has not answered it. After the lease ends the service either delivers it again or terminates it. If this repeats, ${LISTENER_DELIVERY_HOLD_RELEASE_REMEDIES[newestHeldBack.reason]}.`
|
|
45518
46110
|
);
|
|
45519
46111
|
}
|
|
45520
46112
|
lines.push(
|
|
@@ -46055,6 +46647,9 @@ async function runConfiguredListener(options) {
|
|
|
46055
46647
|
},
|
|
46056
46648
|
routeMode,
|
|
46057
46649
|
deferOverChars,
|
|
46650
|
+
/* One delivery may hold the seat for one turn budget, not for the
|
|
46651
|
+
whole 15-minute lease. Same lever, so the two cannot drift. */
|
|
46652
|
+
deliveryHoldBudgetMs: turnBudgetMs,
|
|
46058
46653
|
pendingMainQueue,
|
|
46059
46654
|
fetcher: httpClient.fetch
|
|
46060
46655
|
});
|
|
@@ -47301,6 +47896,174 @@ async function runFeedback(args) {
|
|
|
47301
47896
|
"Feedback recorded for the operators of this deployment. It is stored durably with your workspace and identity attached, and it is read when they review feedback - there is no reply channel, so nothing further will happen in this session.\n"
|
|
47302
47897
|
);
|
|
47303
47898
|
}
|
|
47899
|
+
async function channelRows(context) {
|
|
47900
|
+
if (context.selected.kind === "agent") {
|
|
47901
|
+
throw new Error(CHANNEL_LIST_NEEDS_HUMAN_MESSAGE);
|
|
47902
|
+
}
|
|
47903
|
+
const read = async () => await listChannelsAsHuman(
|
|
47904
|
+
context.cloud,
|
|
47905
|
+
context.selected.human.accessToken,
|
|
47906
|
+
context.selected.selectedWorkspace
|
|
47907
|
+
);
|
|
47908
|
+
try {
|
|
47909
|
+
return await read();
|
|
47910
|
+
} catch (error) {
|
|
47911
|
+
if (error instanceof ChannelListError && error.noResponse) return await read();
|
|
47912
|
+
throw error;
|
|
47913
|
+
}
|
|
47914
|
+
}
|
|
47915
|
+
function channelSelectorKind(selector) {
|
|
47916
|
+
if (UUID_RE23.test(selector)) return "id";
|
|
47917
|
+
const problem = channelSelectorProblem(selector);
|
|
47918
|
+
if (problem !== null) throw new Error(problem);
|
|
47919
|
+
return "name";
|
|
47920
|
+
}
|
|
47921
|
+
async function resolveChannelSelector(context, selector, kind) {
|
|
47922
|
+
if (kind === "id") return selector.toLowerCase();
|
|
47923
|
+
if (context.selected.kind === "agent") {
|
|
47924
|
+
throw new Error(CHANNEL_SELECTOR_NEEDS_ID_MESSAGE);
|
|
47925
|
+
}
|
|
47926
|
+
const rows3 = await channelRows(context);
|
|
47927
|
+
const match = findChannelBySlug(rows3, selector);
|
|
47928
|
+
if (match === null) throw new Error(unknownChannelMessage(selector, rows3));
|
|
47929
|
+
return match.channel_id;
|
|
47930
|
+
}
|
|
47931
|
+
async function sendChannelCommand(context, command2) {
|
|
47932
|
+
const client = new ThinCommandClient(context.cloud);
|
|
47933
|
+
const result = await client.sendChannel({
|
|
47934
|
+
workspaceId: context.selected.selectedWorkspace,
|
|
47935
|
+
command: command2,
|
|
47936
|
+
credential: context.selected.bearer
|
|
47937
|
+
});
|
|
47938
|
+
return result.channel;
|
|
47939
|
+
}
|
|
47940
|
+
var CHANNEL_FLAGS = [
|
|
47941
|
+
...TARGET_FLAGS,
|
|
47942
|
+
"workspace-id",
|
|
47943
|
+
...CREDENTIAL_FLAGS,
|
|
47944
|
+
"json"
|
|
47945
|
+
];
|
|
47946
|
+
async function runChannelCreate(args) {
|
|
47947
|
+
const name = args.positionals[2];
|
|
47948
|
+
if (name === void 0) {
|
|
47949
|
+
throw new UsageError("cswarm channel create needs a channel name");
|
|
47950
|
+
}
|
|
47951
|
+
args.assertShape([...CHANNEL_FLAGS, "purpose"], 3);
|
|
47952
|
+
const problem = channelSlugProblem(name);
|
|
47953
|
+
if (problem !== null) throw new Error(problem);
|
|
47954
|
+
const purposeInput = args.optional("purpose");
|
|
47955
|
+
const purpose = purposeInput === void 0 ? void 0 : purposeInput.trim();
|
|
47956
|
+
if (purpose !== void 0 && purpose.length > CHANNEL_PURPOSE_MAX) {
|
|
47957
|
+
throw new Error(
|
|
47958
|
+
`A channel purpose is at most ${CHANNEL_PURPOSE_MAX} characters.`
|
|
47959
|
+
);
|
|
47960
|
+
}
|
|
47961
|
+
const context = await fileContext(args, ["purpose"], 3);
|
|
47962
|
+
const channel = await sendChannelCommand(context, {
|
|
47963
|
+
kind: "channel_create",
|
|
47964
|
+
slug: normalizeChannelSlug(name),
|
|
47965
|
+
...purpose === void 0 || purpose.length === 0 ? {} : { purpose }
|
|
47966
|
+
});
|
|
47967
|
+
if (args.has("json")) {
|
|
47968
|
+
printJson({ workspace_id: channel.workspace_id, channel });
|
|
47969
|
+
return;
|
|
47970
|
+
}
|
|
47971
|
+
process.stdout.write(
|
|
47972
|
+
`Channel ${channel.slug} created. Everyone in this workspace can read it and post to it; a channel is where a message is filed, not who may see it.
|
|
47973
|
+
Post to it with cswarm note "<text>" --channel ${channel.slug}
|
|
47974
|
+
Read it with cswarm feed --channel ${channel.slug}
|
|
47975
|
+
Its id, which rename and archive take: ${channel.channel_id}
|
|
47976
|
+
`
|
|
47977
|
+
);
|
|
47978
|
+
}
|
|
47979
|
+
async function runChannelLs(args) {
|
|
47980
|
+
args.assertShape([...CHANNEL_FLAGS, "include-archived"], 2);
|
|
47981
|
+
const context = await fileContext(args, ["include-archived"], 2);
|
|
47982
|
+
const rows3 = await channelRows(context);
|
|
47983
|
+
const includeArchived = args.has("include-archived");
|
|
47984
|
+
if (args.has("json")) {
|
|
47985
|
+
printJson({
|
|
47986
|
+
workspace_id: context.selected.selectedWorkspace,
|
|
47987
|
+
channels: includeArchived ? rows3 : rows3.filter((row) => row.archived_at === null)
|
|
47988
|
+
});
|
|
47989
|
+
return;
|
|
47990
|
+
}
|
|
47991
|
+
process.stdout.write(renderChannelList(rows3, { includeArchived }));
|
|
47992
|
+
}
|
|
47993
|
+
async function runChannelRename(args) {
|
|
47994
|
+
const selector = args.positionals[2];
|
|
47995
|
+
const nextName = args.positionals[3];
|
|
47996
|
+
if (selector === void 0 || nextName === void 0) {
|
|
47997
|
+
throw new UsageError(
|
|
47998
|
+
"cswarm channel rename needs the channel and its new name"
|
|
47999
|
+
);
|
|
48000
|
+
}
|
|
48001
|
+
args.assertShape([...CHANNEL_FLAGS], 4);
|
|
48002
|
+
const selectorKind = channelSelectorKind(selector);
|
|
48003
|
+
const problem = channelSlugProblem(nextName);
|
|
48004
|
+
if (problem !== null) throw new Error(problem);
|
|
48005
|
+
const context = await fileContext(args, [], 4);
|
|
48006
|
+
const channelId = await resolveChannelSelector(context, selector, selectorKind);
|
|
48007
|
+
const channel = await sendChannelCommand(context, {
|
|
48008
|
+
kind: "channel_rename",
|
|
48009
|
+
channel_id: channelId,
|
|
48010
|
+
slug: normalizeChannelSlug(nextName)
|
|
48011
|
+
});
|
|
48012
|
+
if (args.has("json")) {
|
|
48013
|
+
printJson({ workspace_id: channel.workspace_id, channel });
|
|
48014
|
+
return;
|
|
48015
|
+
}
|
|
48016
|
+
process.stdout.write(
|
|
48017
|
+
`Channel renamed to ${channel.slug}. Every message already filed in it is unchanged and its id has not moved.
|
|
48018
|
+
Post to it with cswarm note "<text>" --channel ${channel.slug}
|
|
48019
|
+
Its id: ${channel.channel_id}
|
|
48020
|
+
`
|
|
48021
|
+
);
|
|
48022
|
+
}
|
|
48023
|
+
async function runChannelArchive(args) {
|
|
48024
|
+
const selector = args.positionals[2];
|
|
48025
|
+
if (selector === void 0) {
|
|
48026
|
+
throw new UsageError("cswarm channel archive needs the channel");
|
|
48027
|
+
}
|
|
48028
|
+
args.assertShape([...CHANNEL_FLAGS], 3);
|
|
48029
|
+
const selectorKind = channelSelectorKind(selector);
|
|
48030
|
+
const context = await fileContext(args, [], 3);
|
|
48031
|
+
const channelId = await resolveChannelSelector(context, selector, selectorKind);
|
|
48032
|
+
const channel = await sendChannelCommand(context, {
|
|
48033
|
+
kind: "channel_archive",
|
|
48034
|
+
channel_id: channelId
|
|
48035
|
+
});
|
|
48036
|
+
if (args.has("json")) {
|
|
48037
|
+
printJson({ workspace_id: channel.workspace_id, channel });
|
|
48038
|
+
return;
|
|
48039
|
+
}
|
|
48040
|
+
process.stdout.write(
|
|
48041
|
+
`Channel ${channel.slug} is archived. It keeps its messages and its links, and it takes no new ones. Archiving it again changes nothing.
|
|
48042
|
+
See it with cswarm channel ls --include-archived
|
|
48043
|
+
Read what is in it with cswarm feed --channel ${channel.slug}
|
|
48044
|
+
`
|
|
48045
|
+
);
|
|
48046
|
+
}
|
|
48047
|
+
var CHANNEL_SUBCOMMANDS = {
|
|
48048
|
+
create: runChannelCreate,
|
|
48049
|
+
ls: runChannelLs,
|
|
48050
|
+
rename: runChannelRename,
|
|
48051
|
+
archive: runChannelArchive
|
|
48052
|
+
};
|
|
48053
|
+
var CHANNEL_SUBCOMMAND_NAMES = Object.keys(
|
|
48054
|
+
CHANNEL_SUBCOMMANDS
|
|
48055
|
+
);
|
|
48056
|
+
async function runChannel(args) {
|
|
48057
|
+
const action = args.positionals[1];
|
|
48058
|
+
const chosen = action === void 0 ? void 0 : CHANNEL_SUBCOMMANDS[action];
|
|
48059
|
+
if (chosen === void 0) {
|
|
48060
|
+
const names = Object.keys(CHANNEL_SUBCOMMANDS);
|
|
48061
|
+
throw new UsageError(
|
|
48062
|
+
`cswarm channel takes ${names.slice(0, -1).join(", ")}, or ${names[names.length - 1]}`
|
|
48063
|
+
);
|
|
48064
|
+
}
|
|
48065
|
+
return await chosen(args);
|
|
48066
|
+
}
|
|
47304
48067
|
async function runFile(args) {
|
|
47305
48068
|
const action = args.positionals[1];
|
|
47306
48069
|
if (action === "put") return await runFilePut(args);
|
|
@@ -47576,6 +48339,10 @@ async function main() {
|
|
|
47576
48339
|
await runFeedback(args);
|
|
47577
48340
|
return;
|
|
47578
48341
|
}
|
|
48342
|
+
if (verb === "channel") {
|
|
48343
|
+
await runChannel(args);
|
|
48344
|
+
return;
|
|
48345
|
+
}
|
|
47579
48346
|
if (verb === "file") {
|
|
47580
48347
|
await runFile(args);
|
|
47581
48348
|
return;
|
|
@@ -47717,6 +48484,7 @@ ${usage()}
|
|
|
47717
48484
|
});
|
|
47718
48485
|
// Annotate the CommonJS export names for ESM import in node:
|
|
47719
48486
|
0 && (module.exports = {
|
|
48487
|
+
CHANNEL_SUBCOMMAND_NAMES,
|
|
47720
48488
|
EXIT_RESTARTABLE,
|
|
47721
48489
|
ListenerUnattendedRefusedError,
|
|
47722
48490
|
TURN_BUDGET_CREDENTIAL_MARGIN_MS,
|
|
@@ -47734,5 +48502,7 @@ ${usage()}
|
|
|
47734
48502
|
replyRefusalHint,
|
|
47735
48503
|
resolveDetachedClaudeExecutable,
|
|
47736
48504
|
resolveDetachedCodexExecutable,
|
|
47737
|
-
resolveTurnBudgetOrDefer
|
|
48505
|
+
resolveTurnBudgetOrDefer,
|
|
48506
|
+
threadReplyMessage,
|
|
48507
|
+
usage
|
|
47738
48508
|
});
|