@ccmsg/cli 0.3.0 → 0.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ccmsg/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "The ccmsg daemon, CLI and agent plugins for one instance (= one config home)",
5
5
  "license": "MIT",
6
6
  "author": "kawaz",
@@ -20,7 +20,7 @@
20
20
  "test": "bun test"
21
21
  },
22
22
  "dependencies": {
23
- "@ccmsg/protocol": "1.9.0"
23
+ "@ccmsg/protocol": "1.10.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/bun": "^1.3.0",
@@ -286,6 +286,21 @@ function endpointOf(file: string, at: string, raw: unknown): Endpoint {
286
286
  return raw as Endpoint;
287
287
  }
288
288
 
289
+ /** `terminal_gateway`'s shape, matched to the contract's `HelloResult` so a
290
+ * value this instance would refuse to report is refused here instead, at
291
+ * startup, rather than on the first `hello`. */
292
+ const TERMINAL_GATEWAY = /^https?:\/\/[^/?#\s]+(\/[^?#\s]*[^/?#\s])?$/;
293
+
294
+ function terminalGatewayOf(file: string, raw: string): string {
295
+ if (!TERMINAL_GATEWAY.test(raw)) {
296
+ throw new ConfigError(
297
+ file,
298
+ `upstream.terminal_gateway must be an http:// or https:// base URL with no trailing slash, got ${raw}`,
299
+ );
300
+ }
301
+ return raw;
302
+ }
303
+
289
304
  function peersOf(file: string, raw: unknown): readonly Endpoint[] {
290
305
  if (raw === undefined) return [];
291
306
  if (!Array.isArray(raw)) throw new ConfigError(file, "peers must be an array of endpoint URLs");
@@ -334,6 +349,9 @@ function upstreamOf(file: string, raw: unknown): UpstreamConfig {
334
349
  }
335
350
  config[name] = value;
336
351
  }
352
+ if (config["terminal_gateway"] !== undefined) {
353
+ config["terminal_gateway"] = terminalGatewayOf(file, config["terminal_gateway"]);
354
+ }
337
355
  const launcher = fields["launcher"];
338
356
  return {
339
357
  ...(config as UpstreamConfig),
@@ -469,6 +469,9 @@ export class Instance {
469
469
  transcript: this.#transcripts,
470
470
  gateway: this.#gateway,
471
471
  terminals: hostTerminalReader(),
472
+ ...(config.upstream.terminal_gateway === undefined
473
+ ? {}
474
+ : { terminalGateway: config.upstream.terminal_gateway }),
472
475
  log: (msg, fields) => {
473
476
  this.log.write(msg, fields);
474
477
  },
@@ -98,6 +98,12 @@ export interface SessionsDeps {
98
98
  * domain cannot judge: a peer's, whose claim is settled by an exchange of its
99
99
  * own rather than by anything a session says (§7.2). */
100
100
  readonly mesh?: MeshSource;
101
+ /** Where a person opens the terminal a session runs in, which `hello` states
102
+ * as `terminal_gateway`. The same value that gates the `terminal` capability
103
+ * (`sessionCapabilities`), so a client told the capability is on is told
104
+ * where to reach it in the same greeting. Absent on an instance with no
105
+ * gateway configured. */
106
+ readonly terminalGateway?: string;
101
107
  }
102
108
 
103
109
  /** What `hello` needs of the mesh: verify the greeting of a peer, and say which
@@ -291,6 +297,9 @@ export class Sessions implements UpstreamResource {
291
297
  capabilities: [...this.deps.capabilities],
292
298
  version: this.deps.version,
293
299
  started_at: this.deps.startedAt,
300
+ ...(this.deps.terminalGateway === undefined
301
+ ? {}
302
+ : { terminal_gateway: this.deps.terminalGateway }),
294
303
  ...(expiresAt === undefined ? {} : { auth_expires_at: expiresAt }),
295
304
  };
296
305
  }