@ours.network/cowork 1.0.7 → 1.0.9-nightly.20260903.9d2f23e

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.5.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.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.
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
@@ -4311,6 +4311,12 @@ var LowerCrockfordUlidSchema = external_exports.string().regex(
4311
4311
  /^[0-7][0-9a-hjkmnp-tv-z]{25}$/,
4312
4312
  "must be a 26-character lowercase Crockford ULID"
4313
4313
  );
4314
+ 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"]);
4316
+ var RuntimeCommandGrantSchema = external_exports.object({
4317
+ caller_cid: ContainerIdSchema,
4318
+ command: RuntimeCommandNameSchema
4319
+ }).strict();
4314
4320
  function isStrictRfc3339(value) {
4315
4321
  const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-](\d{2}):(\d{2}))$/.exec(value);
4316
4322
  if (!match) return false;
@@ -4626,6 +4632,8 @@ var CurrentRoomSchema = external_exports.object({
4626
4632
  seen.add(role);
4627
4633
  }
4628
4634
  }),
4635
+ /** Operator-managed, default-deny grants for the room identity's runtime commands. */
4636
+ command_grants: external_exports.array(RuntimeCommandGrantSchema),
4629
4637
  anonymous: external_exports.boolean(),
4630
4638
  quiet_membership: external_exports.boolean(),
4631
4639
  membership_epoch: external_exports.number().int().nonnegative().safe(),
@@ -4686,6 +4694,25 @@ var CurrentRoomSchema = external_exports.object({
4686
4694
  });
4687
4695
  }
4688
4696
  }
4697
+ const seenCommandGrants = /* @__PURE__ */ new Set();
4698
+ for (const [index, grant] of room.command_grants.entries()) {
4699
+ const key = `${grant.caller_cid}\0${grant.command}`;
4700
+ if (seenCommandGrants.has(key)) {
4701
+ context.addIssue({
4702
+ code: external_exports.ZodIssueCode.custom,
4703
+ path: ["command_grants", index],
4704
+ message: "runtime command grants must be unique by caller CID and command"
4705
+ });
4706
+ }
4707
+ seenCommandGrants.add(key);
4708
+ if (!room.seats.some((seat) => seat.state === "active" && seat.identity === grant.caller_cid)) {
4709
+ context.addIssue({
4710
+ code: external_exports.ZodIssueCode.custom,
4711
+ path: ["command_grants", index, "caller_cid"],
4712
+ message: "runtime command grants require an active room identity"
4713
+ });
4714
+ }
4715
+ }
4689
4716
  for (const [index, seat] of room.seats.entries()) {
4690
4717
  if (seat.replaces_seat === void 0) continue;
4691
4718
  const predecessor = byParticipant.get(seat.replaces_seat);
@@ -4726,6 +4753,7 @@ var RoomSchema = external_exports.preprocess((value) => {
4726
4753
  }
4727
4754
  }
4728
4755
  if (!Object.hasOwn(value, "rest_roles")) patch.rest_roles = [];
4756
+ if (!Object.hasOwn(value, "command_grants")) patch.command_grants = [];
4729
4757
  if (Object.keys(patch).length === 0) return value;
4730
4758
  return { ...value, ...patch };
4731
4759
  }, CurrentRoomSchema);
@@ -4760,7 +4788,6 @@ var PostAsRoleInputSchema = external_exports.object({
4760
4788
  var RestRoleInputSchema = external_exports.object({
4761
4789
  role: RoleSchema
4762
4790
  }).strict();
4763
- var ContainerIdSchema = external_exports.string().regex(/^[0-9a-f]{64}$/i, "must be a 64-character hexadecimal CID").transform((value) => value.toUpperCase());
4764
4791
  var AcceptExternalInviteInputSchema = external_exports.object({
4765
4792
  role: RoleSchema,
4766
4793
  invite: external_exports.string().refine(
@@ -4770,6 +4797,24 @@ var AcceptExternalInviteInputSchema = external_exports.object({
4770
4797
  expected_cid: ContainerIdSchema.optional(),
4771
4798
  replaces_seat: LowerCrockfordUlidSchema.optional()
4772
4799
  }).strict();
4800
+ var ListMembersCommandInputSchema = external_exports.object({}).strict();
4801
+ var CommandIdempotencyKeySchema = external_exports.string().regex(
4802
+ /^[A-Za-z0-9._:-]{1,128}$/,
4803
+ "must contain 1-128 portable idempotency-key characters"
4804
+ );
4805
+ var RemoveMemberCommandInputSchema = external_exports.object({
4806
+ participant_id: LowerCrockfordUlidSchema,
4807
+ expected_membership_epoch: external_exports.number().int().nonnegative().safe(),
4808
+ confirm: external_exports.literal(true),
4809
+ idempotency_key: CommandIdempotencyKeySchema
4810
+ }).strict();
4811
+ var RuntimeCommandAuditSchema = external_exports.object({
4812
+ sender_cid: NonEmptyStringSchema,
4813
+ sender_participant_id: LowerCrockfordUlidSchema,
4814
+ request_wire_id: NonEmptyStringSchema,
4815
+ idempotency_key: CommandIdempotencyKeySchema,
4816
+ expected_membership_epoch: external_exports.number().int().nonnegative().safe()
4817
+ }).strict();
4773
4818
  var AuthorSnapshotSchema = external_exports.object({
4774
4819
  identity: NonEmptyStringSchema,
4775
4820
  display_name: NonEmptyStringSchema,
@@ -4880,7 +4925,8 @@ var MembershipIntentShape = {
4880
4925
  role: RoleSchema,
4881
4926
  alias: NonEmptyStringSchema.optional(),
4882
4927
  epoch: PositiveSafeIntegerSchema,
4883
- notify: external_exports.boolean()
4928
+ notify: external_exports.boolean(),
4929
+ command: RuntimeCommandAuditSchema.optional()
4884
4930
  };
4885
4931
  var MembershipResultShape = {
4886
4932
  kind: external_exports.literal("membership_result"),
@@ -5057,7 +5103,9 @@ var NATIVE_MUTATION_METHODS = /* @__PURE__ */ new Set([
5057
5103
  "room.recover.confirm",
5058
5104
  "room.rebind",
5059
5105
  "room.participant.remove",
5060
- "room.participant.replace"
5106
+ "room.participant.replace",
5107
+ "room.command.grant",
5108
+ "room.command.revoke"
5061
5109
  ]);
5062
5110
  function rpcTimeoutForMethod(method) {
5063
5111
  return NATIVE_MUTATION_METHODS.has(method) ? NATIVE_RPC_TIMEOUT_MS : RPC_TIMEOUT_MS;
@@ -5101,6 +5149,9 @@ Room commands:
5101
5149
  list
5102
5150
  show <room-id>
5103
5151
  participants <room-id>
5152
+ command-grants <room-id>
5153
+ command-grant <room-id> <caller-cid> <list-members|remove-member>
5154
+ command-revoke <room-id> <caller-cid> <list-members|remove-member>
5104
5155
  history <room-id> [--after <seq>] [--limit <n>] [--view operator|participant]
5105
5156
  message <room-id> --text <text>
5106
5157
  say <room-id> --role <label> --text <text>
@@ -5296,6 +5347,25 @@ async function roomRequest(command, args) {
5296
5347
  const [roomId] = exactPositionals(parseOptions(args), 1, `room ${command}`);
5297
5348
  return { method: `room.${command}`, params: { room_id: roomId } };
5298
5349
  }
5350
+ case "command-grants": {
5351
+ const [roomId] = exactPositionals(parseOptions(args), 1, "room command-grants");
5352
+ return { method: "room.command.grants", params: { room_id: roomId } };
5353
+ }
5354
+ case "command-grant":
5355
+ case "command-revoke": {
5356
+ const [roomId, callerCid, runtimeCommand] = exactPositionals(
5357
+ parseOptions(args),
5358
+ 3,
5359
+ `room ${command}`
5360
+ );
5361
+ if (runtimeCommand !== "list-members" && runtimeCommand !== "remove-member") {
5362
+ usageError("runtime command must be list-members or remove-member");
5363
+ }
5364
+ return {
5365
+ method: command === "command-grant" ? "room.command.grant" : "room.command.revoke",
5366
+ params: { room_id: roomId, caller_cid: callerCid, command: runtimeCommand }
5367
+ };
5368
+ }
5299
5369
  case "history": {
5300
5370
  const parsed = parseOptions(args, ["--after", "--limit", "--view"]);
5301
5371
  const [roomId] = exactPositionals(parsed, 1, "room history");
@@ -5719,7 +5789,11 @@ function serviceEnvironment(config) {
5719
5789
  };
5720
5790
  }
5721
5791
  function validateServiceConfiguration(config) {
5722
- const values = { cowork_cli: SELF, ...serviceEnvironment(config) };
5792
+ const values = {
5793
+ node_executable: process.execPath,
5794
+ cowork_cli: SELF,
5795
+ ...serviceEnvironment(config)
5796
+ };
5723
5797
  for (const [label, value] of Object.entries(values)) {
5724
5798
  if (/[\x00-\x1f\x7f-\x9f]/.test(value)) {
5725
5799
  throw new CliError(EXIT.invalidState, "invalid_state", `${label} contains a control character unsafe for a service definition`);
@@ -5731,7 +5805,7 @@ function systemdValueQuote(value) {
5731
5805
  }
5732
5806
  function systemdExecutableQuote(value) {
5733
5807
  if (value.includes('"') || /[\0\n\r]/.test(value)) {
5734
- throw new CliError(EXIT.invalidState, "invalid_state", "cowork CLI path contains a character unsupported by systemd ExecStart");
5808
+ throw new CliError(EXIT.invalidState, "invalid_state", "node_executable contains a character unsupported by systemd ExecStart");
5735
5809
  }
5736
5810
  return `"${value.replaceAll("\\", "\\\\").replaceAll("%", "%%")}"`;
5737
5811
  }
@@ -5761,7 +5835,7 @@ StartLimitIntervalSec=0
5761
5835
 
5762
5836
  [Service]
5763
5837
  Type=simple
5764
- ExecStart=${systemdExecutableQuote(SELF)} serve
5838
+ ExecStart=${systemdExecutableQuote(process.execPath)} ${systemdValueQuote(SELF)} serve
5765
5839
  ${environment}
5766
5840
  Restart=on-failure
5767
5841
  RestartSec=5
@@ -5798,7 +5872,7 @@ function installLaunchd(config) {
5798
5872
  <plist version="1.0"><dict>
5799
5873
  <key>Label</key><string>${LAUNCHD_LABEL}</string>
5800
5874
  <key>ProgramArguments</key><array>
5801
- <string>${xml(SELF)}</string><string>serve</string>
5875
+ <string>${xml(process.execPath)}</string><string>${xml(SELF)}</string><string>serve</string>
5802
5876
  </array>
5803
5877
  <key>EnvironmentVariables</key><dict>
5804
5878
  ${environment}