commonswarm 0.1.42 → 0.1.43
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 +212 -16
- package/package.json +1 -1
package/cswarm.cjs
CHANGED
|
@@ -23119,6 +23119,40 @@ async function listFilesAsHuman(target2, accessToken, workspaceId2, fetcher = fe
|
|
|
23119
23119
|
return body;
|
|
23120
23120
|
}
|
|
23121
23121
|
|
|
23122
|
+
// src/cloud/brain.ts
|
|
23123
|
+
var BRAIN_FILE_PREFIX = "brain--";
|
|
23124
|
+
var BRAIN_FILE_SUFFIX = ".md";
|
|
23125
|
+
var BRAIN_TOPIC_MAX_LENGTH = 255 - BRAIN_FILE_PREFIX.length - BRAIN_FILE_SUFFIX.length;
|
|
23126
|
+
var BRAIN_TOPIC_RE = /^[a-z0-9][a-z0-9._-]*$/;
|
|
23127
|
+
var BrainTopicError = class extends Error {
|
|
23128
|
+
name = "BrainTopicError";
|
|
23129
|
+
};
|
|
23130
|
+
function canonicalBrainTopic(value) {
|
|
23131
|
+
const topic = value.trim().toLowerCase();
|
|
23132
|
+
if (topic.length < 1 || topic.length > BRAIN_TOPIC_MAX_LENGTH || !BRAIN_TOPIC_RE.test(topic)) {
|
|
23133
|
+
throw new BrainTopicError(
|
|
23134
|
+
`brain topics use ${BRAIN_TOPIC_MAX_LENGTH} or fewer lowercase letters, numbers, dots, dashes, or underscores; start with a letter or number`
|
|
23135
|
+
);
|
|
23136
|
+
}
|
|
23137
|
+
return topic;
|
|
23138
|
+
}
|
|
23139
|
+
function brainFileName(value) {
|
|
23140
|
+
return `${BRAIN_FILE_PREFIX}${canonicalBrainTopic(value)}${BRAIN_FILE_SUFFIX}`;
|
|
23141
|
+
}
|
|
23142
|
+
function brainTopicFromFileName(name) {
|
|
23143
|
+
const lower = name.toLowerCase();
|
|
23144
|
+
if (!lower.startsWith(BRAIN_FILE_PREFIX) || !lower.endsWith(BRAIN_FILE_SUFFIX)) {
|
|
23145
|
+
return null;
|
|
23146
|
+
}
|
|
23147
|
+
const topic = lower.slice(BRAIN_FILE_PREFIX.length, -BRAIN_FILE_SUFFIX.length);
|
|
23148
|
+
try {
|
|
23149
|
+
return canonicalBrainTopic(topic);
|
|
23150
|
+
} catch (error) {
|
|
23151
|
+
if (error instanceof BrainTopicError) return null;
|
|
23152
|
+
throw error;
|
|
23153
|
+
}
|
|
23154
|
+
}
|
|
23155
|
+
|
|
23122
23156
|
// src/cloud/feedback.ts
|
|
23123
23157
|
var FeedbackTransportError = class extends Error {
|
|
23124
23158
|
name = "FeedbackTransportError";
|
|
@@ -39237,8 +39271,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
|
|
|
39237
39271
|
AGENT_CREDENTIAL_MESSAGE_D088
|
|
39238
39272
|
];
|
|
39239
39273
|
function packageVersion() {
|
|
39240
|
-
if ("0.1.
|
|
39241
|
-
return "0.1.
|
|
39274
|
+
if ("0.1.43".length > 0) {
|
|
39275
|
+
return "0.1.43";
|
|
39242
39276
|
}
|
|
39243
39277
|
try {
|
|
39244
39278
|
const value = JSON.parse(
|
|
@@ -39370,6 +39404,9 @@ Usage:
|
|
|
39370
39404
|
cswarm file get <name|file-id> [--version <n>] [--out <local-path>] [--force] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39371
39405
|
cswarm file rm <name|file-id> [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39372
39406
|
cswarm file restore <name|file-id> [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39407
|
+
cswarm brain ls [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39408
|
+
cswarm brain get <topic> [--version <n>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39409
|
+
cswarm brain put <topic> [<markdown-path>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json] # without a path, reads Markdown from stdin
|
|
39373
39410
|
cswarm feedback "<text>" --kind bug|idea|friction [--about <ref>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] ${agentCredential2} [--json]
|
|
39374
39411
|
cswarm listen start ${requiredAgentCredential} [--url <url> --anon-key <key>] --workspace-id <uuid> --provider grok|opencode|claude|codex [--cwd <absolute-path>] [--model <model>] [--effort <level>] [--permissions deny|allow] [--grok-executable <path>] [--opencode-executable <path>] [--claude-executable <path>] [--codex-executable <path>] [--turn-budget <duration>] [--route worker|main|split] [--defer-over <chars>] [--foreground] [--json]
|
|
39375
39412
|
cswarm listen status [--url <url> --anon-key <key>] --workspace-id <uuid> --principal-id <uuid> [--json]
|
|
@@ -39415,7 +39452,8 @@ Credential selection for command/dogfood:
|
|
|
39415
39452
|
signal command/read only -- either form
|
|
39416
39453
|
receipt reads only -- either form
|
|
39417
39454
|
inbox --notify persists a per-agent cursor -- needs principal_id
|
|
39418
|
-
file put, file ls, file get, file rm, file restore
|
|
39455
|
+
file put, file ls, file get, file rm, file restore,
|
|
39456
|
+
brain ls, brain get, brain put
|
|
39419
39457
|
read and command, nothing persisted -- either form
|
|
39420
39458
|
feedback command only, nothing persisted -- either form
|
|
39421
39459
|
command, dogfood
|
|
@@ -43113,17 +43151,7 @@ async function resolveFileSelector(context, selector) {
|
|
|
43113
43151
|
}
|
|
43114
43152
|
return match.file_id;
|
|
43115
43153
|
}
|
|
43116
|
-
async function
|
|
43117
|
-
const localPath = args.positionals[2];
|
|
43118
|
-
if (!localPath) throw new UsageError("cswarm file put needs a local path");
|
|
43119
|
-
const context = await fileContext(args, ["name"], 3);
|
|
43120
|
-
let bytes;
|
|
43121
|
-
try {
|
|
43122
|
-
bytes = (0, import_node_fs7.readFileSync)(localPath);
|
|
43123
|
-
} catch {
|
|
43124
|
-
throw new Error(`could not read ${localPath}; check the path and permissions`);
|
|
43125
|
-
}
|
|
43126
|
-
const name = args.optional("name") ?? (0, import_node_path20.basename)(localPath);
|
|
43154
|
+
async function uploadNamedFile(context, name, bytes) {
|
|
43127
43155
|
if (bytes.byteLength > FILE_MAX_VERSION_BYTES) {
|
|
43128
43156
|
throw new Error(
|
|
43129
43157
|
`this file is ${formatFileSize(bytes.byteLength)}; the per-file limit is ${formatFileSize(FILE_MAX_VERSION_BYTES)}, so the upload was not started`
|
|
@@ -43156,13 +43184,26 @@ async function runFilePut(args) {
|
|
|
43156
43184
|
await onceRetried(
|
|
43157
43185
|
() => putObject(context.cloud, created.upload_path, bytes, contentType)
|
|
43158
43186
|
);
|
|
43159
|
-
|
|
43187
|
+
return await onceRetried(
|
|
43160
43188
|
() => fileVersionCommit({ ...send, commandId: commitCommandId }, {
|
|
43161
43189
|
fileId: created.file_id,
|
|
43162
43190
|
versionId: created.version_id,
|
|
43163
43191
|
sha256: sha256Hex(bytes)
|
|
43164
43192
|
})
|
|
43165
43193
|
);
|
|
43194
|
+
}
|
|
43195
|
+
async function runFilePut(args) {
|
|
43196
|
+
const localPath = args.positionals[2];
|
|
43197
|
+
if (!localPath) throw new UsageError("cswarm file put needs a local path");
|
|
43198
|
+
const context = await fileContext(args, ["name"], 3);
|
|
43199
|
+
let bytes;
|
|
43200
|
+
try {
|
|
43201
|
+
bytes = (0, import_node_fs7.readFileSync)(localPath);
|
|
43202
|
+
} catch {
|
|
43203
|
+
throw new Error(`could not read ${localPath}; check the path and permissions`);
|
|
43204
|
+
}
|
|
43205
|
+
const name = args.optional("name") ?? (0, import_node_path20.basename)(localPath);
|
|
43206
|
+
const committed = await uploadNamedFile(context, name, bytes);
|
|
43166
43207
|
if (args.has("json")) {
|
|
43167
43208
|
process.stdout.write(`${JSON.stringify(committed, null, 2)}
|
|
43168
43209
|
`);
|
|
@@ -43288,6 +43329,157 @@ async function runFileRestore(args) {
|
|
|
43288
43329
|
`
|
|
43289
43330
|
);
|
|
43290
43331
|
}
|
|
43332
|
+
async function brainRows(context) {
|
|
43333
|
+
const rows3 = await fileRows(context);
|
|
43334
|
+
return rows3.filter((row) => row.tombstoned_at === null).flatMap((file) => {
|
|
43335
|
+
const topic = brainTopicFromFileName(file.name);
|
|
43336
|
+
return topic === null ? [] : [{ topic, file }];
|
|
43337
|
+
}).sort((left, right) => left.topic.localeCompare(right.topic));
|
|
43338
|
+
}
|
|
43339
|
+
async function readBrainMarkdownFromStdin() {
|
|
43340
|
+
if (process.stdin.isTTY) {
|
|
43341
|
+
throw new UsageError(
|
|
43342
|
+
"cswarm brain put needs a Markdown path or piped Markdown on stdin"
|
|
43343
|
+
);
|
|
43344
|
+
}
|
|
43345
|
+
const chunks = [];
|
|
43346
|
+
let size2 = 0;
|
|
43347
|
+
for await (const chunk of process.stdin) {
|
|
43348
|
+
const bytes2 = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
43349
|
+
size2 += bytes2.byteLength;
|
|
43350
|
+
if (size2 > FILE_MAX_VERSION_BYTES) {
|
|
43351
|
+
throw new Error(
|
|
43352
|
+
`brain topic input is larger than ${formatFileSize(FILE_MAX_VERSION_BYTES)}; nothing was uploaded`
|
|
43353
|
+
);
|
|
43354
|
+
}
|
|
43355
|
+
chunks.push(bytes2);
|
|
43356
|
+
}
|
|
43357
|
+
const bytes = Buffer.concat(chunks);
|
|
43358
|
+
if (bytes.byteLength === 0) {
|
|
43359
|
+
throw new UsageError("cswarm brain put received empty Markdown; nothing was uploaded");
|
|
43360
|
+
}
|
|
43361
|
+
return bytes;
|
|
43362
|
+
}
|
|
43363
|
+
function decodeBrainMarkdown(bytes) {
|
|
43364
|
+
try {
|
|
43365
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
43366
|
+
} catch {
|
|
43367
|
+
throw new Error("the brain topic is not valid UTF-8 Markdown");
|
|
43368
|
+
}
|
|
43369
|
+
}
|
|
43370
|
+
async function runBrainLs(args) {
|
|
43371
|
+
const context = await fileContext(args, [], 2);
|
|
43372
|
+
const topics = await brainRows(context);
|
|
43373
|
+
if (args.has("json")) {
|
|
43374
|
+
process.stdout.write(
|
|
43375
|
+
`${JSON.stringify({
|
|
43376
|
+
workspace_id: context.selected.selectedWorkspace,
|
|
43377
|
+
topics: topics.map(({ topic, file }) => ({ topic, ...file }))
|
|
43378
|
+
}, null, 2)}
|
|
43379
|
+
`
|
|
43380
|
+
);
|
|
43381
|
+
return;
|
|
43382
|
+
}
|
|
43383
|
+
if (topics.length === 0) {
|
|
43384
|
+
process.stdout.write(
|
|
43385
|
+
"No brain topics yet. Add one with: cswarm brain put <topic> <markdown-path>.\n"
|
|
43386
|
+
);
|
|
43387
|
+
return;
|
|
43388
|
+
}
|
|
43389
|
+
process.stdout.write(`Brain topics (${topics.length}):
|
|
43390
|
+
`);
|
|
43391
|
+
for (const { topic, file } of topics) {
|
|
43392
|
+
const versions = `${file.current_version} ${file.current_version === 1 ? "version" : "versions"}`;
|
|
43393
|
+
const author = file.uploaded_by ? `${file.uploaded_by_kind ?? file.created_by_kind} ${file.uploaded_by.slice(0, 8)}` : file.created_by_kind;
|
|
43394
|
+
process.stdout.write(
|
|
43395
|
+
`- ${topic} \xB7 ${versions} \xB7 updated ${file.committed_at ?? file.created_at} \xB7 by ${author}
|
|
43396
|
+
`
|
|
43397
|
+
);
|
|
43398
|
+
}
|
|
43399
|
+
}
|
|
43400
|
+
async function runBrainGet(args) {
|
|
43401
|
+
const requestedTopic = args.positionals[2];
|
|
43402
|
+
if (!requestedTopic) throw new UsageError("cswarm brain get needs a topic");
|
|
43403
|
+
const topic = canonicalBrainTopic(requestedTopic);
|
|
43404
|
+
const context = await fileContext(args, ["version"], 3);
|
|
43405
|
+
const row = (await brainRows(context)).find((candidate) => candidate.topic === topic);
|
|
43406
|
+
if (!row) {
|
|
43407
|
+
throw new Error(
|
|
43408
|
+
`no brain topic named "${sanitizeDisplayLabel(topic, "that topic")}" exists; run cswarm brain ls to see the current topics`
|
|
43409
|
+
);
|
|
43410
|
+
}
|
|
43411
|
+
const versionN = args.has("version") ? integer2(args, "version", { minimum: 1 }) : null;
|
|
43412
|
+
const grant = await fileDownloadUrl({
|
|
43413
|
+
target: context.cloud,
|
|
43414
|
+
workspaceId: context.selected.selectedWorkspace,
|
|
43415
|
+
credential: context.selected.bearer
|
|
43416
|
+
}, { fileId: row.file.file_id, versionN });
|
|
43417
|
+
const content = decodeBrainMarkdown(
|
|
43418
|
+
await getObject(context.cloud, grant.download_path)
|
|
43419
|
+
);
|
|
43420
|
+
if (args.has("json")) {
|
|
43421
|
+
process.stdout.write(`${JSON.stringify({
|
|
43422
|
+
topic,
|
|
43423
|
+
file_id: grant.file_id,
|
|
43424
|
+
version_n: grant.version_n,
|
|
43425
|
+
updated_at: row.file.committed_at,
|
|
43426
|
+
updated_by_kind: row.file.uploaded_by_kind,
|
|
43427
|
+
updated_by: row.file.uploaded_by,
|
|
43428
|
+
content
|
|
43429
|
+
}, null, 2)}
|
|
43430
|
+
`);
|
|
43431
|
+
return;
|
|
43432
|
+
}
|
|
43433
|
+
process.stdout.write(content.endsWith("\n") ? content : `${content}
|
|
43434
|
+
`);
|
|
43435
|
+
}
|
|
43436
|
+
async function runBrainPut(args) {
|
|
43437
|
+
const requestedTopic = args.positionals[2];
|
|
43438
|
+
if (!requestedTopic) throw new UsageError("cswarm brain put needs a topic");
|
|
43439
|
+
if (args.positionals.length > 4) {
|
|
43440
|
+
throw new UsageError("cswarm brain put takes one topic and, optionally, one Markdown path");
|
|
43441
|
+
}
|
|
43442
|
+
const topic = canonicalBrainTopic(requestedTopic);
|
|
43443
|
+
const localPath = args.positionals[3];
|
|
43444
|
+
if (!localPath && args.has("agent-token-stdin")) {
|
|
43445
|
+
throw new UsageError(
|
|
43446
|
+
"cswarm brain put cannot read both the credential and Markdown from stdin; use --agent-token-file or pass a Markdown path"
|
|
43447
|
+
);
|
|
43448
|
+
}
|
|
43449
|
+
const context = await fileContext(args, [], args.positionals.length);
|
|
43450
|
+
let bytes;
|
|
43451
|
+
if (localPath) {
|
|
43452
|
+
try {
|
|
43453
|
+
bytes = (0, import_node_fs7.readFileSync)(localPath);
|
|
43454
|
+
} catch {
|
|
43455
|
+
throw new Error(`could not read ${localPath}; check the path and permissions`);
|
|
43456
|
+
}
|
|
43457
|
+
if (bytes.byteLength === 0) {
|
|
43458
|
+
throw new UsageError("cswarm brain put received an empty Markdown file; nothing was uploaded");
|
|
43459
|
+
}
|
|
43460
|
+
} else {
|
|
43461
|
+
bytes = await readBrainMarkdownFromStdin();
|
|
43462
|
+
}
|
|
43463
|
+
decodeBrainMarkdown(bytes);
|
|
43464
|
+
const committed = await uploadNamedFile(context, brainFileName(topic), bytes);
|
|
43465
|
+
if (args.has("json")) {
|
|
43466
|
+
process.stdout.write(`${JSON.stringify({ topic, ...committed }, null, 2)}
|
|
43467
|
+
`);
|
|
43468
|
+
return;
|
|
43469
|
+
}
|
|
43470
|
+
process.stdout.write(
|
|
43471
|
+
`Saved brain topic ${topic} as version ${committed.version_n}. It is now visible to everyone in this workspace.
|
|
43472
|
+
Read it with: cswarm brain get ${topic}
|
|
43473
|
+
`
|
|
43474
|
+
);
|
|
43475
|
+
}
|
|
43476
|
+
async function runBrain(args) {
|
|
43477
|
+
const action = args.positionals[1];
|
|
43478
|
+
if (action === "ls") return await runBrainLs(args);
|
|
43479
|
+
if (action === "get") return await runBrainGet(args);
|
|
43480
|
+
if (action === "put") return await runBrainPut(args);
|
|
43481
|
+
throw new UsageError("cswarm brain takes ls, get, or put");
|
|
43482
|
+
}
|
|
43291
43483
|
async function runFeedback(args) {
|
|
43292
43484
|
const body = args.positionals[1];
|
|
43293
43485
|
if (!body) {
|
|
@@ -43605,6 +43797,10 @@ async function main() {
|
|
|
43605
43797
|
await runFile(args);
|
|
43606
43798
|
return;
|
|
43607
43799
|
}
|
|
43800
|
+
if (verb === "brain") {
|
|
43801
|
+
await runBrain(args);
|
|
43802
|
+
return;
|
|
43803
|
+
}
|
|
43608
43804
|
if (verb === "members") {
|
|
43609
43805
|
await runMembers(args);
|
|
43610
43806
|
return;
|
|
@@ -43697,7 +43893,7 @@ main().catch((error) => {
|
|
|
43697
43893
|
if (error instanceof WorkspaceCliError) {
|
|
43698
43894
|
const structured = error.structured();
|
|
43699
43895
|
const verb = process.argv[2];
|
|
43700
|
-
const json = process.argv.includes("--json") && (verb === "status" || verb === "workspaces" || verb === "use" || verb === "working-on" || verb === "note" || verb === "ask" || verb === "reply" || verb === "receipt" || verb === "feed" || verb === "inbox");
|
|
43896
|
+
const json = process.argv.includes("--json") && (verb === "status" || verb === "workspaces" || verb === "use" || verb === "working-on" || verb === "note" || verb === "ask" || verb === "reply" || verb === "receipt" || verb === "feed" || verb === "inbox" || verb === "file" || verb === "brain");
|
|
43701
43897
|
if (json) {
|
|
43702
43898
|
process.stdout.write(`${JSON.stringify(structured, null, 2)}
|
|
43703
43899
|
`);
|