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.
Files changed (2) hide show
  1. package/cswarm.cjs +212 -16
  2. 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.42".length > 0) {
39241
- return "0.1.42";
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 runFilePut(args) {
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
- const committed = await onceRetried(
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
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commonswarm",
3
- "version": "0.1.42",
3
+ "version": "0.1.43",
4
4
  "description": "CommonSwarm CLI — coordination for teams where people and AI agents work side by side.",
5
5
  "bin": {
6
6
  "cswarm": "cswarm.cjs"