@skrr-ai/cli 0.1.35 → 0.1.36

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 (41) hide show
  1. package/dist/commands/balance/index.js +2 -1
  2. package/dist/commands/balance/overage.d.ts +12 -0
  3. package/dist/commands/balance/overage.js +43 -0
  4. package/dist/commands/balance/plan.d.ts +11 -0
  5. package/dist/commands/balance/plan.js +59 -0
  6. package/dist/commands/balance/usage/events.d.ts +15 -0
  7. package/dist/commands/balance/usage/events.js +69 -0
  8. package/dist/commands/balance/usage.js +13 -2
  9. package/dist/commands/commitments/export.d.ts +3 -0
  10. package/dist/commands/commitments/export.js +59 -14
  11. package/dist/commands/learning/export.d.ts +5 -1
  12. package/dist/commands/learning/export.js +85 -21
  13. package/dist/commands/tasks/export.d.ts +6 -2
  14. package/dist/commands/tasks/export.js +69 -21
  15. package/dist/commands/tones/export.d.ts +3 -1
  16. package/dist/commands/tones/export.js +50 -21
  17. package/dist/lib/artifact-export.d.ts +84 -0
  18. package/dist/lib/artifact-export.js +122 -0
  19. package/dist/lib/balance.d.ts +13 -1
  20. package/dist/lib/balance.js +30 -0
  21. package/dist/lib/web-url.d.ts +2 -0
  22. package/dist/lib/web-url.js +2 -0
  23. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +15 -10
  24. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +18 -11
  25. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +9 -3
  26. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +15 -10
  27. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +18 -11
  28. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +9 -3
  29. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  30. package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
  31. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/loopback-server.d.ts +2 -0
  32. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/loopback-server.js +8 -3
  33. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.d.ts +2 -0
  34. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.js +6 -1
  35. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/loopback-server.d.ts +2 -0
  36. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/loopback-server.js +9 -4
  37. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.d.ts +2 -0
  38. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.js +7 -2
  39. package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
  40. package/oclif.manifest.json +6974 -6657
  41. package/package.json +1 -1
@@ -20,7 +20,9 @@
20
20
  *
21
21
  * 1. set `displayName` and `provider`;
22
22
  * 2. move the old `provider` to the front of `aliases`;
23
- * 3. leave `wireProvider` on the spelling installed daemons still speak;
23
+ * 3. set `wireProvider` to the old spelling as a literal — installed daemons
24
+ * still speak it — and derive it from `provider` again only in the wire
25
+ * phase, once every peer accepts the new one;
24
26
  * 4. regenerate `first-party-harness.json`
25
27
  * (`node --import tsx scripts/emit-first-party-harness-json.mjs`);
26
28
  * 5. ship the data migration, which is generic over `aliases`.
@@ -34,9 +36,11 @@
34
36
  * canonical at the edge. Nothing new is ever written with one.
35
37
  * `wireProvider` What the server and a daemon say to EACH OTHER — the
36
38
  * capability a daemon advertises, `daemon:<p>:request|response`,
37
- * the `provider` of a session RPC. It moves LAST, because a
38
- * daemon on a user's machine may be any version and the server
39
- * cannot tell which one sent a request.
39
+ * the `provider` of a session RPC. It moves after the flip and
40
+ * BEFORE contract, because a daemon on a user's machine may be
41
+ * any version and the server cannot tell which one sent a
42
+ * request: only a released daemon that already advertises the
43
+ * canonical capability lets the old one age out of the fleet.
40
44
  *
41
45
  * The old code treated those three as one string, which is why no single
42
46
  * change could rename it safely. See
@@ -96,21 +100,24 @@ function defineIdentity(identity) {
96
100
  }
97
101
  return Object.freeze({ ...identity, aliases: Object.freeze([...identity.aliases]) });
98
102
  }
103
+ /** The canonical slug. Written once, and read by every field that names the current spelling. */
104
+ const PROVIDER = 'skrr-code';
99
105
  /**
100
106
  * THE identity — the only literal product name in the codebase.
101
107
  *
102
- * Phase: FLIPPED. `skrr-code` is canonical — stored, returned and rendered.
103
- * `sky-code` is still accepted on every ingress, and it is still the WIRE
104
- * spelling: daemons on users' machines may predate the rename, so the server
105
- * and a daemon keep exchanging `sky-code` until contract (OSK-8663) empties
106
- * `aliases` and moves `wireProvider`.
108
+ * Phase: WIRE MOVED. `skrr-code` is canonical — stored, returned, rendered — and
109
+ * it is now also what the server and a daemon say to each other. `sky-code` is
110
+ * still accepted on every ingress, so a daemon released before this change
111
+ * (which advertises and answers on `sky-code`) keeps working against a server
112
+ * that speaks `skrr-code`, and the reverse. Contract (OSK-8663) empties
113
+ * `aliases` once no connected daemon still advertises the old capability.
107
114
  */
108
115
  const IDENTITY = {
109
116
  displayName: 'skrr Code',
110
117
  command: 'skrr code',
111
- provider: 'skrr-code',
118
+ provider: PROVIDER,
112
119
  aliases: ['sky-code'],
113
- wireProvider: 'sky-code',
120
+ wireProvider: PROVIDER,
114
121
  };
115
122
  exports.FIRST_PARTY_HARNESS = defineIdentity(IDENTITY);
116
123
  /** The canonical slug with its literal type, for `as const` tuples and keyed tables. */
@@ -150,7 +150,13 @@ function firstPartyHarnessFeedCandidates(env = process.env) {
150
150
  * caret range. An installed CLI that picks up this version must still find the
151
151
  * names it was compiled against, or it crashes at import. These aliases leave in
152
152
  * the contract phase (OSK-8663), with a minor-version bump — never a patch.
153
+ *
154
+ * The ones that name a feed name the LEGACY one, the spelling a caller compiled
155
+ * against them was built for — the retired alias, not `wireProvider`, which has
156
+ * moved to canonical.
153
157
  */
158
+ /** The spelling the deprecated names were written for. */
159
+ const LEGACY_SPELLING = firstPartyHarness_js_1.FIRST_PARTY_HARNESS.aliases[0] ?? firstPartyHarness_js_1.FIRST_PARTY_HARNESS.provider;
154
160
  /** @deprecated Use `FIRST_PARTY_HARNESS_CHANNELS`. */
155
161
  exports.SKY_CODE_CHANNELS = exports.FIRST_PARTY_HARNESS_CHANNELS;
156
162
  /** @deprecated Use `DEFAULT_FIRST_PARTY_HARNESS_CHANNEL`. */
@@ -158,7 +164,7 @@ exports.DEFAULT_SKY_CODE_CHANNEL = exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL;
158
164
  /** @deprecated Read the channel with `resolveFirstPartyHarnessChannel`; the variable has a neutral name now. */
159
165
  exports.SKY_CODE_CHANNEL_ENV = firstPartyHarness_js_1.FIRST_PARTY_HARNESS_ENV.channel.legacy[0];
160
166
  /** @deprecated Use `firstPartyHarnessManifestFile()`. Names the legacy file. */
161
- exports.SKY_CODE_MANIFEST_FILE = (0, firstPartyHarness_js_1.firstPartyHarnessManifestFile)(firstPartyHarness_js_1.FIRST_PARTY_HARNESS.wireProvider);
167
+ exports.SKY_CODE_MANIFEST_FILE = (0, firstPartyHarness_js_1.firstPartyHarnessManifestFile)(LEGACY_SPELLING);
162
168
  /** @deprecated Use `isFirstPartyHarnessChannel`. */
163
169
  exports.isSkyCodeChannel = isFirstPartyHarnessChannel;
164
170
  /** @deprecated Use `resolveFirstPartyHarnessChannel`. */
@@ -166,7 +172,7 @@ exports.resolveSkyCodeChannel = resolveFirstPartyHarnessChannel;
166
172
  /** @deprecated Use `firstPartyHarnessChannelPrefix`. */
167
173
  exports.skyCodeChannelPrefix = firstPartyHarnessChannelPrefix;
168
174
  /** @deprecated Use `firstPartyHarnessDefaultFeedBase()`. Names the legacy feed. */
169
- exports.DEFAULT_SKY_CODE_FEED_BASE = (0, firstPartyHarness_js_1.firstPartyHarnessDefaultFeedBase)(firstPartyHarness_js_1.FIRST_PARTY_HARNESS.wireProvider);
175
+ exports.DEFAULT_SKY_CODE_FEED_BASE = (0, firstPartyHarness_js_1.firstPartyHarnessDefaultFeedBase)(LEGACY_SPELLING);
170
176
  /** @deprecated Read the feed with `firstPartyHarnessFeedBase`; the variable has a neutral name now. */
171
177
  exports.SKY_CODE_FEED_BASE_ENV = firstPartyHarness_js_1.FIRST_PARTY_HARNESS_ENV.updateFeedBase.legacy[0];
172
178
  /**
@@ -176,5 +182,5 @@ exports.SKY_CODE_FEED_BASE_ENV = firstPartyHarness_js_1.FIRST_PARTY_HARNESS_ENV.
176
182
  */
177
183
  function skyCodeFeedBase(env = process.env) {
178
184
  const override = (0, firstPartyHarness_js_1.readFirstPartyHarnessEnv)(env, 'updateFeedBase').value;
179
- return (override || (0, firstPartyHarness_js_1.firstPartyHarnessDefaultFeedBase)(firstPartyHarness_js_1.FIRST_PARTY_HARNESS.wireProvider)).replace(/\/+$/, '');
185
+ return (override || (0, firstPartyHarness_js_1.firstPartyHarnessDefaultFeedBase)(LEGACY_SPELLING)).replace(/\/+$/, '');
180
186
  }
@@ -19,7 +19,9 @@
19
19
  *
20
20
  * 1. set `displayName` and `provider`;
21
21
  * 2. move the old `provider` to the front of `aliases`;
22
- * 3. leave `wireProvider` on the spelling installed daemons still speak;
22
+ * 3. set `wireProvider` to the old spelling as a literal — installed daemons
23
+ * still speak it — and derive it from `provider` again only in the wire
24
+ * phase, once every peer accepts the new one;
23
25
  * 4. regenerate `first-party-harness.json`
24
26
  * (`node --import tsx scripts/emit-first-party-harness-json.mjs`);
25
27
  * 5. ship the data migration, which is generic over `aliases`.
@@ -33,9 +35,11 @@
33
35
  * canonical at the edge. Nothing new is ever written with one.
34
36
  * `wireProvider` What the server and a daemon say to EACH OTHER — the
35
37
  * capability a daemon advertises, `daemon:<p>:request|response`,
36
- * the `provider` of a session RPC. It moves LAST, because a
37
- * daemon on a user's machine may be any version and the server
38
- * cannot tell which one sent a request.
38
+ * the `provider` of a session RPC. It moves after the flip and
39
+ * BEFORE contract, because a daemon on a user's machine may be
40
+ * any version and the server cannot tell which one sent a
41
+ * request: only a released daemon that already advertises the
42
+ * canonical capability lets the old one age out of the fleet.
39
43
  *
40
44
  * The old code treated those three as one string, which is why no single
41
45
  * change could rename it safely. See
@@ -63,18 +67,19 @@ export interface FirstPartyHarnessIdentity {
63
67
  /**
64
68
  * THE identity — the only literal product name in the codebase.
65
69
  *
66
- * Phase: FLIPPED. `skrr-code` is canonical — stored, returned and rendered.
67
- * `sky-code` is still accepted on every ingress, and it is still the WIRE
68
- * spelling: daemons on users' machines may predate the rename, so the server
69
- * and a daemon keep exchanging `sky-code` until contract (OSK-8663) empties
70
- * `aliases` and moves `wireProvider`.
70
+ * Phase: WIRE MOVED. `skrr-code` is canonical — stored, returned, rendered — and
71
+ * it is now also what the server and a daemon say to each other. `sky-code` is
72
+ * still accepted on every ingress, so a daemon released before this change
73
+ * (which advertises and answers on `sky-code`) keeps working against a server
74
+ * that speaks `skrr-code`, and the reverse. Contract (OSK-8663) empties
75
+ * `aliases` once no connected daemon still advertises the old capability.
71
76
  */
72
77
  declare const IDENTITY: {
73
78
  readonly displayName: "skrr Code";
74
79
  readonly command: "skrr code";
75
80
  readonly provider: "skrr-code";
76
81
  readonly aliases: readonly ["sky-code"];
77
- readonly wireProvider: "sky-code";
82
+ readonly wireProvider: "skrr-code";
78
83
  };
79
84
  /** The canonical slug, as a literal type. */
80
85
  export type FirstPartyHarnessCanonicalProvider = typeof IDENTITY.provider;
@@ -19,7 +19,9 @@
19
19
  *
20
20
  * 1. set `displayName` and `provider`;
21
21
  * 2. move the old `provider` to the front of `aliases`;
22
- * 3. leave `wireProvider` on the spelling installed daemons still speak;
22
+ * 3. set `wireProvider` to the old spelling as a literal — installed daemons
23
+ * still speak it — and derive it from `provider` again only in the wire
24
+ * phase, once every peer accepts the new one;
23
25
  * 4. regenerate `first-party-harness.json`
24
26
  * (`node --import tsx scripts/emit-first-party-harness-json.mjs`);
25
27
  * 5. ship the data migration, which is generic over `aliases`.
@@ -33,9 +35,11 @@
33
35
  * canonical at the edge. Nothing new is ever written with one.
34
36
  * `wireProvider` What the server and a daemon say to EACH OTHER — the
35
37
  * capability a daemon advertises, `daemon:<p>:request|response`,
36
- * the `provider` of a session RPC. It moves LAST, because a
37
- * daemon on a user's machine may be any version and the server
38
- * cannot tell which one sent a request.
38
+ * the `provider` of a session RPC. It moves after the flip and
39
+ * BEFORE contract, because a daemon on a user's machine may be
40
+ * any version and the server cannot tell which one sent a
41
+ * request: only a released daemon that already advertises the
42
+ * canonical capability lets the old one age out of the fleet.
39
43
  *
40
44
  * The old code treated those three as one string, which is why no single
41
45
  * change could rename it safely. See
@@ -67,21 +71,24 @@ function defineIdentity(identity) {
67
71
  }
68
72
  return Object.freeze({ ...identity, aliases: Object.freeze([...identity.aliases]) });
69
73
  }
74
+ /** The canonical slug. Written once, and read by every field that names the current spelling. */
75
+ const PROVIDER = 'skrr-code';
70
76
  /**
71
77
  * THE identity — the only literal product name in the codebase.
72
78
  *
73
- * Phase: FLIPPED. `skrr-code` is canonical — stored, returned and rendered.
74
- * `sky-code` is still accepted on every ingress, and it is still the WIRE
75
- * spelling: daemons on users' machines may predate the rename, so the server
76
- * and a daemon keep exchanging `sky-code` until contract (OSK-8663) empties
77
- * `aliases` and moves `wireProvider`.
79
+ * Phase: WIRE MOVED. `skrr-code` is canonical — stored, returned, rendered — and
80
+ * it is now also what the server and a daemon say to each other. `sky-code` is
81
+ * still accepted on every ingress, so a daemon released before this change
82
+ * (which advertises and answers on `sky-code`) keeps working against a server
83
+ * that speaks `skrr-code`, and the reverse. Contract (OSK-8663) empties
84
+ * `aliases` once no connected daemon still advertises the old capability.
78
85
  */
79
86
  const IDENTITY = {
80
87
  displayName: 'skrr Code',
81
88
  command: 'skrr code',
82
- provider: 'skrr-code',
89
+ provider: PROVIDER,
83
90
  aliases: ['sky-code'],
84
- wireProvider: 'sky-code',
91
+ wireProvider: PROVIDER,
85
92
  };
86
93
  export const FIRST_PARTY_HARNESS = defineIdentity(IDENTITY);
87
94
  /** The canonical slug with its literal type, for `as const` tuples and keyed tables. */
@@ -140,7 +140,13 @@ export function firstPartyHarnessFeedCandidates(env = process.env) {
140
140
  * caret range. An installed CLI that picks up this version must still find the
141
141
  * names it was compiled against, or it crashes at import. These aliases leave in
142
142
  * the contract phase (OSK-8663), with a minor-version bump — never a patch.
143
+ *
144
+ * The ones that name a feed name the LEGACY one, the spelling a caller compiled
145
+ * against them was built for — the retired alias, not `wireProvider`, which has
146
+ * moved to canonical.
143
147
  */
148
+ /** The spelling the deprecated names were written for. */
149
+ const LEGACY_SPELLING = FIRST_PARTY_HARNESS.aliases[0] ?? FIRST_PARTY_HARNESS.provider;
144
150
  /** @deprecated Use `FIRST_PARTY_HARNESS_CHANNELS`. */
145
151
  export const SKY_CODE_CHANNELS = FIRST_PARTY_HARNESS_CHANNELS;
146
152
  /** @deprecated Use `DEFAULT_FIRST_PARTY_HARNESS_CHANNEL`. */
@@ -148,7 +154,7 @@ export const DEFAULT_SKY_CODE_CHANNEL = DEFAULT_FIRST_PARTY_HARNESS_CHANNEL;
148
154
  /** @deprecated Read the channel with `resolveFirstPartyHarnessChannel`; the variable has a neutral name now. */
149
155
  export const SKY_CODE_CHANNEL_ENV = FIRST_PARTY_HARNESS_ENV.channel.legacy[0];
150
156
  /** @deprecated Use `firstPartyHarnessManifestFile()`. Names the legacy file. */
151
- export const SKY_CODE_MANIFEST_FILE = firstPartyHarnessManifestFile(FIRST_PARTY_HARNESS.wireProvider);
157
+ export const SKY_CODE_MANIFEST_FILE = firstPartyHarnessManifestFile(LEGACY_SPELLING);
152
158
  /** @deprecated Use `isFirstPartyHarnessChannel`. */
153
159
  export const isSkyCodeChannel = isFirstPartyHarnessChannel;
154
160
  /** @deprecated Use `resolveFirstPartyHarnessChannel`. */
@@ -156,7 +162,7 @@ export const resolveSkyCodeChannel = resolveFirstPartyHarnessChannel;
156
162
  /** @deprecated Use `firstPartyHarnessChannelPrefix`. */
157
163
  export const skyCodeChannelPrefix = firstPartyHarnessChannelPrefix;
158
164
  /** @deprecated Use `firstPartyHarnessDefaultFeedBase()`. Names the legacy feed. */
159
- export const DEFAULT_SKY_CODE_FEED_BASE = firstPartyHarnessDefaultFeedBase(FIRST_PARTY_HARNESS.wireProvider);
165
+ export const DEFAULT_SKY_CODE_FEED_BASE = firstPartyHarnessDefaultFeedBase(LEGACY_SPELLING);
160
166
  /** @deprecated Read the feed with `firstPartyHarnessFeedBase`; the variable has a neutral name now. */
161
167
  export const SKY_CODE_FEED_BASE_ENV = FIRST_PARTY_HARNESS_ENV.updateFeedBase.legacy[0];
162
168
  /**
@@ -166,5 +172,5 @@ export const SKY_CODE_FEED_BASE_ENV = FIRST_PARTY_HARNESS_ENV.updateFeedBase.leg
166
172
  */
167
173
  export function skyCodeFeedBase(env = process.env) {
168
174
  const override = readFirstPartyHarnessEnv(env, 'updateFeedBase').value;
169
- return (override || firstPartyHarnessDefaultFeedBase(FIRST_PARTY_HARNESS.wireProvider)).replace(/\/+$/, '');
175
+ return (override || firstPartyHarnessDefaultFeedBase(LEGACY_SPELLING)).replace(/\/+$/, '');
170
176
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skrr-ai/auth-core",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "main": "dist/cjs/index.js",
5
5
  "types": "dist/esm/index.d.ts",
6
6
  "exports": {
@@ -3,7 +3,7 @@
3
3
  "version": "0.9.5",
4
4
  "main": "index.js",
5
5
  "dependencies": {
6
- "@skrr-ai/auth-core": "^0.1.2"
6
+ "@skrr-ai/auth-core": "^0.2.0"
7
7
  },
8
8
  "_vendoredBy": "cli/scripts/vendor-monorepo-deps.js"
9
9
  }
@@ -38,6 +38,8 @@ export interface LoopbackServerOptions {
38
38
  port?: number;
39
39
  /** Private Unix-domain socket transport. Mutually exclusive with TCP host/port. */
40
40
  socketPath?: string;
41
+ /** Optional trusted group that may connect to the per-session Unix socket. */
42
+ socketGroupId?: number;
41
43
  /** URL advertised to a client that uses `fetch(..., { unix: socketPath })`. */
42
44
  advertisedBaseUrl?: string;
43
45
  /** Bounded before buffering so a local child cannot exhaust daemon memory. */
@@ -87,9 +87,14 @@ class ModelProxyLoopbackServer {
87
87
  const addr = server.address();
88
88
  this.boundPort = typeof addr === 'object' && addr ? addr.port : this.opts.port;
89
89
  if (this.opts.socketPath && process.platform !== 'win32') {
90
- // The parent directory is owned by the daemon; this closes the
91
- // remaining filesystem-discovery surface on the socket itself.
92
- (0, node_fs_1.chmodSync)(this.opts.socketPath, 0o600);
90
+ const sharedGroup = Number.isSafeInteger(this.opts.socketGroupId);
91
+ if (sharedGroup) {
92
+ (0, node_fs_1.chownSync)(this.opts.socketPath, typeof process.getuid === 'function' ? process.getuid() : -1, this.opts.socketGroupId);
93
+ }
94
+ // Private by default. A Dedicated Runtime explicitly names its
95
+ // root-owned workload group because the daemon and harness use
96
+ // different uids; no other local user gains access.
97
+ (0, node_fs_1.chmodSync)(this.opts.socketPath, sharedGroup ? 0o660 : 0o600);
93
98
  }
94
99
  resolve();
95
100
  };
@@ -115,6 +115,8 @@ export interface ManagedInferenceBrokerOptions {
115
115
  /** Immutable managed lease contract issued alongside the virtual key. */
116
116
  modelScope?: ManagedInferenceModelScope;
117
117
  fetchImpl?: FetchLike;
118
+ /** Trusted cross-uid group for a Dedicated Runtime harness child. */
119
+ socketGroupId?: number;
118
120
  }
119
121
  export interface ManagedInferenceBrokerSession {
120
122
  /** Safe-to-deliver child environment. It intentionally never includes the gateway key. */
@@ -378,12 +378,17 @@ async function startManagedInferenceBroker(options) {
378
378
  // can be deeply nested. The random mkdtemp directory is private to this
379
379
  // daemon process; the socket itself is chmod 0600 by the listener.
380
380
  const socketDirectory = (0, node_fs_1.mkdtempSync)('/tmp/oversky-inference-');
381
- (0, node_fs_1.chmodSync)(socketDirectory, 0o700);
381
+ const sharedSocketGroup = Number.isSafeInteger(options.socketGroupId);
382
+ if (sharedSocketGroup) {
383
+ (0, node_fs_1.chownSync)(socketDirectory, typeof process.getuid === 'function' ? process.getuid() : -1, options.socketGroupId);
384
+ }
385
+ (0, node_fs_1.chmodSync)(socketDirectory, sharedSocketGroup ? 0o750 : 0o700);
382
386
  const socketPath = (0, node_path_1.join)(socketDirectory, 'broker.sock');
383
387
  const server = new loopback_server_js_1.ModelProxyLoopbackServer({
384
388
  relayToken,
385
389
  forwarder,
386
390
  socketPath,
391
+ ...(sharedSocketGroup ? { socketGroupId: options.socketGroupId } : {}),
387
392
  advertisedBaseUrl: exports.INFERENCE_BROKER_ORIGIN,
388
393
  ...(envelope ? { maxBodyBytes: envelope.maxInputBytes } : {}),
389
394
  });
@@ -38,6 +38,8 @@ export interface LoopbackServerOptions {
38
38
  port?: number;
39
39
  /** Private Unix-domain socket transport. Mutually exclusive with TCP host/port. */
40
40
  socketPath?: string;
41
+ /** Optional trusted group that may connect to the per-session Unix socket. */
42
+ socketGroupId?: number;
41
43
  /** URL advertised to a client that uses `fetch(..., { unix: socketPath })`. */
42
44
  advertisedBaseUrl?: string;
43
45
  /** Bounded before buffering so a local child cannot exhaust daemon memory. */
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { createServer } from 'node:http';
17
17
  import { timingSafeEqual } from 'node:crypto';
18
- import { chmodSync, unlinkSync } from 'node:fs';
18
+ import { chmodSync, chownSync, unlinkSync } from 'node:fs';
19
19
  import { MODEL_PROXY_ERROR, MODEL_PROXY_MAX_BODY_BYTES, ModelProxyError, } from './contract.js';
20
20
  class RequestBodyTooLargeError extends Error {
21
21
  maxBodyBytes;
@@ -84,9 +84,14 @@ export class ModelProxyLoopbackServer {
84
84
  const addr = server.address();
85
85
  this.boundPort = typeof addr === 'object' && addr ? addr.port : this.opts.port;
86
86
  if (this.opts.socketPath && process.platform !== 'win32') {
87
- // The parent directory is owned by the daemon; this closes the
88
- // remaining filesystem-discovery surface on the socket itself.
89
- chmodSync(this.opts.socketPath, 0o600);
87
+ const sharedGroup = Number.isSafeInteger(this.opts.socketGroupId);
88
+ if (sharedGroup) {
89
+ chownSync(this.opts.socketPath, typeof process.getuid === 'function' ? process.getuid() : -1, this.opts.socketGroupId);
90
+ }
91
+ // Private by default. A Dedicated Runtime explicitly names its
92
+ // root-owned workload group because the daemon and harness use
93
+ // different uids; no other local user gains access.
94
+ chmodSync(this.opts.socketPath, sharedGroup ? 0o660 : 0o600);
90
95
  }
91
96
  resolve();
92
97
  };
@@ -115,6 +115,8 @@ export interface ManagedInferenceBrokerOptions {
115
115
  /** Immutable managed lease contract issued alongside the virtual key. */
116
116
  modelScope?: ManagedInferenceModelScope;
117
117
  fetchImpl?: FetchLike;
118
+ /** Trusted cross-uid group for a Dedicated Runtime harness child. */
119
+ socketGroupId?: number;
118
120
  }
119
121
  export interface ManagedInferenceBrokerSession {
120
122
  /** Safe-to-deliver child environment. It intentionally never includes the gateway key. */
@@ -10,7 +10,7 @@
10
10
  * endpoint while it is alive.
11
11
  */
12
12
  import { randomBytes, timingSafeEqual } from 'node:crypto';
13
- import { chmodSync, mkdtempSync, rmSync } from 'node:fs';
13
+ import { chmodSync, chownSync, mkdtempSync, rmSync } from 'node:fs';
14
14
  import { join } from 'node:path';
15
15
  import { ModelProxyLoopbackServer } from './loopback-server.js';
16
16
  import { MODEL_PROXY_ERROR, ModelProxyError } from './contract.js';
@@ -368,12 +368,17 @@ export async function startManagedInferenceBroker(options) {
368
368
  // can be deeply nested. The random mkdtemp directory is private to this
369
369
  // daemon process; the socket itself is chmod 0600 by the listener.
370
370
  const socketDirectory = mkdtempSync('/tmp/oversky-inference-');
371
- chmodSync(socketDirectory, 0o700);
371
+ const sharedSocketGroup = Number.isSafeInteger(options.socketGroupId);
372
+ if (sharedSocketGroup) {
373
+ chownSync(socketDirectory, typeof process.getuid === 'function' ? process.getuid() : -1, options.socketGroupId);
374
+ }
375
+ chmodSync(socketDirectory, sharedSocketGroup ? 0o750 : 0o700);
372
376
  const socketPath = join(socketDirectory, 'broker.sock');
373
377
  const server = new ModelProxyLoopbackServer({
374
378
  relayToken,
375
379
  forwarder,
376
380
  socketPath,
381
+ ...(sharedSocketGroup ? { socketGroupId: options.socketGroupId } : {}),
377
382
  advertisedBaseUrl: INFERENCE_BROKER_ORIGIN,
378
383
  ...(envelope ? { maxBodyBytes: envelope.maxInputBytes } : {}),
379
384
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skrr-ai/inference-broker",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "main": "dist/cjs/index.js",
5
5
  "types": "dist/cjs/index.d.ts",
6
6
  "exports": {