@ours.network/cowork 1.1.1 → 1.1.3

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/README.md CHANGED
@@ -15,7 +15,7 @@ The localhost HTTP console has no authentication. Keep it bound to `127.0.0.1`;
15
15
 
16
16
  The same listener describes its own room management REST API: `http://127.0.0.1:3052/openapi.json` is the OpenAPI 3.1 document and `http://127.0.0.1:3052/docs` is the browser UI for it. Both are read-only, load no remote assets, and follow the console's loopback-only exposure rules.
17
17
 
18
- Start or install the shared daemon with `@ours.network/cli` 2.7.0 before starting cowork. Cowork never embeds, starts, stops, or silently substitutes an ours daemon. It uses the SDK-standard shared selection (the default `~/.ours` daemon, `OURS_CONFIG`, or a coherent `OURS_PORT` plus `OURS_STATE_DIR` selection) and fails clearly when that daemon is unavailable or mismatched.
18
+ Start or install the shared daemon with `@ours.network/cli` 2.7.2 before starting cowork. Cowork never embeds, starts, stops, or silently substitutes an ours daemon. It uses the SDK-standard shared selection (the default `~/.ours` daemon, `OURS_CONFIG`, or a coherent `OURS_PORT` plus `OURS_STATE_DIR` selection) and fails clearly when that daemon is unavailable or mismatched.
19
19
 
20
20
  The shared daemon retains application payload history outside its protocol packets: each identity has a `history.sqlite3` database and immutable content-addressed file blobs. Cowork authorizes unread metadata by authenticated CID, reads the corresponding persistent history or blob, durably archives the room item and its complete fan-out, and only then advances that exact SDK unread item. There is no packet-inbox fallback, defer queue, host outbox, or cross-store transaction.
21
21
 
package/dist/cli.js CHANGED
@@ -4061,6 +4061,37 @@ var coerce = {
4061
4061
  };
4062
4062
  var NEVER = INVALID;
4063
4063
 
4064
+ // src/consumer-commands.ts
4065
+ var ConsumerCommandNameSchema = external_exports.string().max(128).regex(/^consumer\.[a-z0-9][a-z0-9.-]*[a-z0-9]$/);
4066
+ var ConsumerDefinitionSchema = external_exports.object({
4067
+ name: ConsumerCommandNameSchema,
4068
+ description: external_exports.string().min(1).max(1024),
4069
+ input_schema: external_exports.record(external_exports.unknown()),
4070
+ handler: external_exports.string().min(1).max(128)
4071
+ }).strict();
4072
+ var StoredConsumerDefinitionSchema = ConsumerDefinitionSchema.extend({
4073
+ source: external_exports.enum(["rest", "local"]),
4074
+ revision: external_exports.number().int().positive().safe()
4075
+ }).strict();
4076
+ var ConsumerConfigurationSchema = external_exports.object({
4077
+ handlers: external_exports.array(external_exports.object({
4078
+ id: external_exports.string().min(1).max(128),
4079
+ url: external_exports.string().url(),
4080
+ token_file: external_exports.string().min(1)
4081
+ }).strict()).max(64),
4082
+ definitions_file: external_exports.string().min(1).optional(),
4083
+ timeout_ms: external_exports.number().int().min(100).max(3e4).default(5e3)
4084
+ }).strict();
4085
+ var MAX_CONSUMER_ARGUMENT_BYTES = 64 * 1024;
4086
+ var MAX_CONSUMER_RESPONSE_BYTES = 256 * 1024;
4087
+ var LocalFileSchema = external_exports.object({
4088
+ version: external_exports.literal(1),
4089
+ rooms: external_exports.array(external_exports.object({
4090
+ room_id: external_exports.string().regex(/^[0-7][0-9a-hjkmnp-tv-z]{25}$/),
4091
+ commands: external_exports.array(ConsumerDefinitionSchema).max(64)
4092
+ }).strict()).max(256)
4093
+ }).strict();
4094
+
4064
4095
  // src/config.ts
4065
4096
  var DIRECTORY_MODE = 448;
4066
4097
  var FILE_MODE = 384;
@@ -4068,6 +4099,7 @@ var NO_FOLLOW = nodeFs.constants.O_NOFOLLOW ?? 0;
4068
4099
  var CoworkConfigSchema = external_exports.object({
4069
4100
  version: external_exports.literal(1),
4070
4101
  stateDir: external_exports.string().min(1),
4102
+ consumer_commands: ConsumerConfigurationSchema.optional(),
4071
4103
  rest: external_exports.object({
4072
4104
  enabled: external_exports.boolean(),
4073
4105
  port: external_exports.number().int().min(1).max(65535)
@@ -4115,6 +4147,7 @@ function loadConfig(env = process.env, io = {}) {
4115
4147
  return CoworkConfigSchema.parse({
4116
4148
  version: 1,
4117
4149
  stateDir: resolve(env.OURS_COWORK_STATE_DIR ?? file.stateDir),
4150
+ ...file.consumer_commands === void 0 ? {} : { consumer_commands: file.consumer_commands },
4118
4151
  rest: {
4119
4152
  enabled: restPort === void 0 ? file.rest.enabled : true,
4120
4153
  port: restPort ?? file.rest.port
@@ -4289,6 +4322,41 @@ function lstatIfPresent(fs2, path) {
4289
4322
 
4290
4323
  // src/contracts.ts
4291
4324
  import { createHash } from "node:crypto";
4325
+
4326
+ // src/command-names.ts
4327
+ var SHARED_ROOM_COMMANDS = [
4328
+ "room.settings",
4329
+ "room.briefing.role.set",
4330
+ "room.briefing.role.delete",
4331
+ "room.invite",
4332
+ "room.accept",
4333
+ "room.rebind",
4334
+ "room.close",
4335
+ "room.delete",
4336
+ "room.participant.remove",
4337
+ "room.revoke",
4338
+ "room.recover",
4339
+ "room.recover.confirm",
4340
+ "room.show",
4341
+ "room.participants",
4342
+ "room.command.grants",
4343
+ "room.command.role.grants",
4344
+ "room.command.role.set",
4345
+ "room.command.grant",
4346
+ "room.command.revoke",
4347
+ "room.history",
4348
+ "room.message",
4349
+ "room.say",
4350
+ "room.role.rest.add",
4351
+ "room.role.rest.remove"
4352
+ ];
4353
+ var RUNTIME_COMMAND_NAMES = [
4354
+ "list-members",
4355
+ "remove-member",
4356
+ ...SHARED_ROOM_COMMANDS
4357
+ ];
4358
+
4359
+ // src/contracts.ts
4292
4360
  var MAX_TEXT_BYTES = 262144;
4293
4361
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
4294
4362
  var MAX_HISTORY_PAGE_BYTES = 3 * 1024 * 1024;
@@ -4312,7 +4380,7 @@ var LowerCrockfordUlidSchema = external_exports.string().regex(
4312
4380
  "must be a 26-character lowercase Crockford ULID"
4313
4381
  );
4314
4382
  var ContainerIdSchema = external_exports.string().regex(/^[0-9a-f]{64}$/i, "must be a 64-character hexadecimal CID").transform((value) => value.toUpperCase());
4315
- var RuntimeCommandNameSchema = external_exports.enum(["list-members", "remove-member"]);
4383
+ var RuntimeCommandNameSchema = external_exports.union([external_exports.enum(RUNTIME_COMMAND_NAMES), ConsumerCommandNameSchema]);
4316
4384
  var RuntimeCommandGrantSchema = external_exports.object({
4317
4385
  caller_cid: ContainerIdSchema,
4318
4386
  command: RuntimeCommandNameSchema
@@ -4654,6 +4722,16 @@ var CurrentRoomSchema = external_exports.object({
4654
4722
  }),
4655
4723
  /** Operator-managed, default-deny grants for the room identity's runtime commands. */
4656
4724
  command_grants: external_exports.array(RuntimeCommandGrantSchema),
4725
+ consumer_commands: external_exports.array(StoredConsumerDefinitionSchema).max(64).optional(),
4726
+ consumer_commands_revision: external_exports.number().int().nonnegative().safe().optional(),
4727
+ lifecycle_request: external_exports.object({
4728
+ request_id: external_exports.string().min(1).max(256),
4729
+ command: external_exports.enum(["room.close", "room.delete"]),
4730
+ caller_cid: ContainerIdSchema,
4731
+ accepted_at: Rfc3339Schema,
4732
+ state: external_exports.enum(["pending", "failed", "completed"]),
4733
+ error: external_exports.literal("lifecycle_failed").optional()
4734
+ }).strict().optional(),
4657
4735
  /** Operator-managed command policy inherited by authenticated active seats of an exact role. */
4658
4736
  role_command_grants: external_exports.array(RuntimeRoleCommandGrantSchema).superRefine((grants, context) => {
4659
4737
  const seen = /* @__PURE__ */ new Set();
@@ -5155,9 +5233,9 @@ Room commands:
5155
5233
  participants <room-id>
5156
5234
  command-grants <room-id>
5157
5235
  role-command-grants <room-id>
5158
- role-command-set <room-id> --role <label> --commands <list-members,remove-member|none>
5159
- command-grant <room-id> <caller-cid> <list-members|remove-member>
5160
- command-revoke <room-id> <caller-cid> <list-members|remove-member>
5236
+ role-command-set <room-id> --role <label> --commands <command,...|none>
5237
+ command-grant <room-id> <caller-cid> <command>
5238
+ command-revoke <room-id> <caller-cid> <command>
5161
5239
  history <room-id> [--after <seq>] [--limit <n>] [--view operator|participant]
5162
5240
  message <room-id> --text <text>
5163
5241
  say <room-id> --role <label> --text <text>
@@ -5353,8 +5431,8 @@ async function roomRequest(command, args) {
5353
5431
  const role = requiredFlag(parsed, "--role", "room role-command-set");
5354
5432
  const value = requiredFlag(parsed, "--commands", "room role-command-set");
5355
5433
  const commands = value === "none" ? [] : value.split(",");
5356
- if (new Set(commands).size !== commands.length || commands.some((item) => item !== "list-members" && item !== "remove-member")) {
5357
- usageError("--commands must be a unique comma-separated subset of list-members,remove-member or none");
5434
+ if (new Set(commands).size !== commands.length || commands.some((item) => !RuntimeCommandNameSchema.safeParse(item).success)) {
5435
+ usageError("--commands must be a unique comma-separated list of built-in command names or none");
5358
5436
  }
5359
5437
  return { method: "room.command.role.set", params: { room_id: roomId, role, commands } };
5360
5438
  }
@@ -5365,8 +5443,8 @@ async function roomRequest(command, args) {
5365
5443
  3,
5366
5444
  `room ${command}`
5367
5445
  );
5368
- if (runtimeCommand !== "list-members" && runtimeCommand !== "remove-member") {
5369
- usageError("runtime command must be list-members or remove-member");
5446
+ if (!RuntimeCommandNameSchema.safeParse(runtimeCommand).success) {
5447
+ usageError("runtime command must be a supported built-in command name");
5370
5448
  }
5371
5449
  return {
5372
5450
  method: command === "command-grant" ? "room.command.grant" : "room.command.revoke",