@sellable/install 0.1.798 → 0.1.799

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.
@@ -3,12 +3,12 @@ FROM ${HERMES_IMAGE}
3
3
 
4
4
  USER root
5
5
 
6
- ARG INSTALLER_PACKAGE=@sellable/install@0.1.798
6
+ ARG INSTALLER_PACKAGE=@sellable/install@0.1.799
7
7
  ARG MCP_PACKAGE=@sellable/mcp@0.1.957
8
8
  ARG INSTALLER_INTEGRITY
9
9
  ARG MCP_INTEGRITY=sha512-tIZuO5IEanT/afamDIWdorUNMdqZA6xnjL9jGAmGqYIlIqkXdHB9slWiwfLfmM44iEp7OT98IBHJlvbpMThk6A==
10
10
 
11
- RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.798" \
11
+ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.799" \
12
12
  && test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.957" \
13
13
  && test -n "${INSTALLER_INTEGRITY}" \
14
14
  && test "$(npm view "${INSTALLER_PACKAGE}" dist.integrity)" = "${INSTALLER_INTEGRITY}" \
@@ -23,9 +23,9 @@ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.798" \
23
23
  && mkdir -p /opt/sellable-agent/install-root /opt/sellable-agent/mcp-root \
24
24
  && npm install --prefix /opt/sellable-agent/install-root --omit=dev --ignore-scripts --no-audit --no-fund "${INSTALLER_PACKAGE}" \
25
25
  && npm install --prefix /opt/sellable-agent/mcp-root --omit=dev --ignore-scripts --no-audit --no-fund "${MCP_PACKAGE}" \
26
- && node --input-type=module -e "import { readFileSync } from 'node:fs'; const install = JSON.parse(readFileSync('/opt/sellable-agent/install-root/node_modules/@sellable/install/package.json')); const mcp = JSON.parse(readFileSync('/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp/package.json')); if (install.name !== '@sellable/install' || install.version !== '0.1.798' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.957') throw new Error('package identity rejected');" \
26
+ && node --input-type=module -e "import { readFileSync } from 'node:fs'; const install = JSON.parse(readFileSync('/opt/sellable-agent/install-root/node_modules/@sellable/install/package.json')); const mcp = JSON.parse(readFileSync('/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp/package.json')); if (install.name !== '@sellable/install' || install.version !== '0.1.799' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.957') throw new Error('package identity rejected');" \
27
27
  && node --input-type=module -e "import { buildExternalRuntimeClosure } from '/opt/sellable-agent/install-root/node_modules/@sellable/install/lib/sellable-agent/external-runtime-builder.mjs'; const built = buildExternalRuntimeClosure({ mcpPackageRoot: '/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp', mcpNodeModulesRoot: '/opt/sellable-agent/mcp-root/node_modules', hermesSourceRoot: '/opt/hermes', ownerUid: 0, ownerGid: 0 }); if (!built.closureDigest) throw new Error('runtime closure rejected');" \
28
- && node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.798', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.957', mcpIntegrity: process.argv[2] }; writeFileSync('/opt/sellable-agent/release.json', JSON.stringify(release) + '\\n', { mode: 0o444 }); chmodSync('/opt/sellable-agent/release.json', 0o444);" "${INSTALLER_INTEGRITY}" "${MCP_INTEGRITY}" \
28
+ && node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.799', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.957', mcpIntegrity: process.argv[2] }; writeFileSync('/opt/sellable-agent/release.json', JSON.stringify(release) + '\\n', { mode: 0o444 }); chmodSync('/opt/sellable-agent/release.json', 0o444);" "${INSTALLER_INTEGRITY}" "${MCP_INTEGRITY}" \
29
29
  && npm cache clean --force
30
30
 
31
31
  COPY entrypoint.sh /usr/local/bin/sellable-agent-container-entrypoint
@@ -3,7 +3,7 @@
3
3
  This image is a dedicated Agent worker/runtime boundary beside an existing Hermes dashboard. It never replaces or reconfigures the dashboard container.
4
4
 
5
5
  Build from this directory after
6
- `@sellable/install@0.1.798` is
6
+ `@sellable/install@0.1.799` is
7
7
  published:
8
8
 
9
9
  ```sh
@@ -8,7 +8,7 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright
8
8
 
9
9
  ARG MCP_PACKAGE=@sellable/mcp@0.1.957
10
10
  ARG MCP_INTEGRITY=sha512-tIZuO5IEanT/afamDIWdorUNMdqZA6xnjL9jGAmGqYIlIqkXdHB9slWiwfLfmM44iEp7OT98IBHJlvbpMThk6A==
11
- ARG INSTALLER_PACKAGE=@sellable/install@0.1.798
11
+ ARG INSTALLER_PACKAGE=@sellable/install@0.1.799
12
12
  ARG ADMIN_MCP_PACKAGE=@sellable/admin-mcp@0.1.202
13
13
  ARG ADMIN_MCP_INTEGRITY=sha512-ym3aye/0vQ8tfULJlKz9JFOxvIQsl1G+n3np+TTgK/tecwNblBo/j3AkTnRqH9iomKzPbZzP+n4tEQsvsU8ByA==
14
14
  ARG TUS_JS_CLIENT_VERSION=4.3.1
@@ -31,17 +31,25 @@ export type ClaudeRunsServer = Server & { stopActiveRuns(): Promise<void> };
31
31
  export type ClaudeRunsApiOptions = {
32
32
  token: string;
33
33
  connection: RunsApiConnection;
34
- execute?: (request: ClaudeRunsRequest) => {
35
- promise: Promise<ClaudeRunsResult>;
36
- stop(): void;
37
- };
34
+ execute?:
35
+ | ((request: ClaudeRunsRequest) => {
36
+ promise: Promise<ClaudeRunsResult>;
37
+ stop(): void;
38
+ })
39
+ | null;
38
40
  audit?: (entry: Record<string, unknown>) => void;
39
41
  maxConcurrency?: number;
40
42
  maxToolConcurrency?: number;
41
43
  toolLanes?: ClaudeRunsToolLanes | null;
42
- checkAuthentication?: () => Promise<
43
- "READY" | "SIGN_IN_REQUIRED" | "UNAVAILABLE"
44
- >;
44
+ checkAuthentication?: (
45
+ configDir: string
46
+ ) => Promise<"READY" | "SIGN_IN_REQUIRED" | "UNAVAILABLE">;
47
+ accounts?: () => string[];
48
+ startRun?: (
49
+ request: ClaudeRunsRequest,
50
+ options: { root: string; configDir: string }
51
+ ) => { promise: Promise<ClaudeRunsResult>; stop(code?: string): void };
52
+ now?: () => number;
45
53
  };
46
54
  export function validateClaudeRunsRequest(
47
55
  body: unknown,
@@ -55,8 +63,14 @@ export function endpointToolNames(options: {
55
63
  export function claudeInvocation(
56
64
  request: ClaudeRunsRequest,
57
65
  root: string,
58
- mcpSpecPath?: string | null
66
+ mcpSpecPath?: string | null,
67
+ configDir?: string
59
68
  ): { command: string; args: string[]; options: SpawnOptions };
69
+ export function claudeAccountDirs(root: string): string[];
70
+ export function claudeUsageLimit(
71
+ stdout: string,
72
+ now?: number
73
+ ): { retryAt: number } | null;
60
74
  export function parseClaudeResult(
61
75
  stdout: string,
62
76
  request: ClaudeRunsRequest
@@ -65,6 +79,7 @@ export function startClaudeRun(
65
79
  request: ClaudeRunsRequest,
66
80
  options: {
67
81
  root: string;
82
+ configDir?: string;
68
83
  spawnImpl?: (...args: any[]) => any;
69
84
  timeoutMs?: number;
70
85
  }
@@ -82,6 +97,7 @@ export function startConfiguredClaudeRunsApis(options?: {
82
97
 
83
98
  export function readClaudeAuthentication(options: {
84
99
  root: string;
100
+ configDir?: string;
85
101
  execFileImpl?: any;
86
102
  timeoutMs?: number;
87
103
  }): Promise<"READY" | "SIGN_IN_REQUIRED" | "UNAVAILABLE">;
@@ -2,7 +2,9 @@ import { execFile, spawn } from "node:child_process";
2
2
  import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
3
3
  import {
4
4
  appendFileSync,
5
+ existsSync,
5
6
  lstatSync,
7
+ readdirSync,
6
8
  readFileSync,
7
9
  rmSync,
8
10
  writeFileSync,
@@ -132,7 +134,12 @@ export function endpointToolNames({
132
134
  return [...names];
133
135
  }
134
136
 
135
- export function claudeInvocation(request, root, mcpSpecPath = null) {
137
+ export function claudeInvocation(
138
+ request,
139
+ root,
140
+ mcpSpecPath = null,
141
+ configDir = `${root}/claude`
142
+ ) {
136
143
  return {
137
144
  command: `${root}/cli/node_modules/.bin/claude`,
138
145
  args: [
@@ -189,8 +196,8 @@ export function claudeInvocation(request, root, mcpSpecPath = null) {
189
196
  stdio: ["pipe", "pipe", "pipe"],
190
197
  env: {
191
198
  PATH: "/usr/local/bin:/usr/bin:/bin",
192
- HOME: `${root}/claude`,
193
- CLAUDE_CONFIG_DIR: `${root}/claude`,
199
+ HOME: configDir,
200
+ CLAUDE_CONFIG_DIR: configDir,
194
201
  LANG: "C.UTF-8",
195
202
  CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
196
203
  },
@@ -202,12 +209,15 @@ export function claudeInvocation(request, root, mcpSpecPath = null) {
202
209
  // this boundary; CLI stdout can contain account identity and is never forwarded.
203
210
  export function readClaudeAuthentication({
204
211
  root,
212
+ configDir = `${root}/claude`,
205
213
  execFileImpl = execFile,
206
214
  timeoutMs = 8_000,
207
215
  } = {}) {
208
216
  const invocation = claudeInvocation(
209
217
  { instructions: "", input: "", model: "claude-opus", effort: "high" },
210
- root
218
+ root,
219
+ null,
220
+ configDir
211
221
  );
212
222
  return new Promise((resolve) => {
213
223
  execFileImpl(
@@ -234,6 +244,90 @@ export function readClaudeAuthentication({
234
244
  });
235
245
  }
236
246
 
247
+ // Each sign-in is its own Claude config folder: the primary `claude`, then
248
+ // `accounts/<name>` in name order. Refresh tokens rotate inside a folder, so a
249
+ // login is never copied between folders.
250
+ export function claudeAccountDirs(root) {
251
+ const dirs = [`${root}/claude`];
252
+ let names = [];
253
+ try {
254
+ names = readdirSync(`${root}/accounts`).sort();
255
+ } catch {
256
+ /* No extra accounts. */
257
+ }
258
+ for (const name of names)
259
+ if (
260
+ /^[a-z0-9][a-z0-9-]{0,62}$/.test(name) &&
261
+ existsSync(`${root}/accounts/${name}/.credentials.json`)
262
+ )
263
+ dirs.push(`${root}/accounts/${name}`);
264
+ return dirs;
265
+ }
266
+
267
+ const MONTHS = "janfebmaraprmayjunjulaugsepoctnovdec";
268
+ const USAGE_LIMIT_RETRY_MS = 15 * 60_000;
269
+
270
+ // Claude Code reports a used-up plan as an API 429 result, for example
271
+ // "You've hit your weekly limit · resets Oct 7, 10am (UTC)". Returns when that
272
+ // account can be tried again, or null for any other result.
273
+ export function claudeUsageLimit(stdout, now = Date.now()) {
274
+ let value;
275
+ try {
276
+ value = JSON.parse(stdout);
277
+ } catch {
278
+ return null;
279
+ }
280
+ if (
281
+ !record(value) ||
282
+ value.is_error !== true ||
283
+ value.api_error_status !== 429
284
+ )
285
+ return null;
286
+ const reset =
287
+ /resets\s+(?:([a-z]{3})[a-z]*\s+(\d{1,2}),?\s+)?(\d{1,2})(?::(\d{2}))?\s*(am|pm)\s*\(UTC\)/i.exec(
288
+ String(value.result ?? "")
289
+ );
290
+ let retryAt = null;
291
+ if (reset) {
292
+ const hour =
293
+ (Number(reset[3]) % 12) + (reset[5].toLowerCase() === "pm" ? 12 : 0);
294
+ const minute = Number(reset[4] ?? 0);
295
+ const today = new Date(now);
296
+ if (reset[1]) {
297
+ const month = MONTHS.indexOf(reset[1].toLowerCase()) / 3;
298
+ if (Number.isInteger(month)) {
299
+ retryAt = Date.UTC(
300
+ today.getUTCFullYear(),
301
+ month,
302
+ Number(reset[2]),
303
+ hour,
304
+ minute
305
+ );
306
+ if (retryAt < now - 86_400_000)
307
+ retryAt = Date.UTC(
308
+ today.getUTCFullYear() + 1,
309
+ month,
310
+ Number(reset[2]),
311
+ hour,
312
+ minute
313
+ );
314
+ }
315
+ } else {
316
+ retryAt = Date.UTC(
317
+ today.getUTCFullYear(),
318
+ today.getUTCMonth(),
319
+ today.getUTCDate(),
320
+ hour,
321
+ minute
322
+ );
323
+ if (retryAt <= now) retryAt += 86_400_000;
324
+ }
325
+ }
326
+ if (retryAt === null || retryAt <= now || retryAt > now + 8 * 86_400_000)
327
+ retryAt = now + USAGE_LIMIT_RETRY_MS;
328
+ return { retryAt };
329
+ }
330
+
237
331
  export function parseClaudeResult(stdout, request) {
238
332
  const result = JSON.parse(stdout);
239
333
  const models = Object.keys(result.modelUsage ?? {});
@@ -272,7 +366,12 @@ export function parseClaudeResult(stdout, request) {
272
366
 
273
367
  export function startClaudeRun(
274
368
  request,
275
- { root, spawnImpl = spawn, timeoutMs = 750_000 } = {}
369
+ {
370
+ root,
371
+ configDir = `${root}/claude`,
372
+ spawnImpl = spawn,
373
+ timeoutMs = 750_000,
374
+ } = {}
276
375
  ) {
277
376
  // The tool server's environment travels in a private per-run file, never
278
377
  // in argv, and is removed when the run ends.
@@ -288,7 +387,7 @@ export function startClaudeRun(
288
387
  const removeSpec = () => {
289
388
  if (mcpSpecPath) rmSync(mcpSpecPath, { force: true });
290
389
  };
291
- const invocation = claudeInvocation(request, root, mcpSpecPath);
390
+ const invocation = claudeInvocation(request, root, mcpSpecPath, configDir);
292
391
  let child;
293
392
  try {
294
393
  child = spawnImpl(invocation.command, invocation.args, invocation.options);
@@ -334,12 +433,16 @@ export function startClaudeRun(
334
433
  clearTimeout(timer);
335
434
  clearTimeout(killTimer);
336
435
  removeSpec();
436
+ const stdout = Buffer.concat(chunks).toString("utf8");
437
+ const limit = reason ? null : claudeUsageLimit(stdout);
438
+ if (limit)
439
+ return reject(
440
+ Object.assign(new Error("claude_runs_usage_limited"), limit)
441
+ );
337
442
  if (reason || code !== 0)
338
443
  return reject(new Error(reason ?? "claude_runs_cli_failed"));
339
444
  try {
340
- resolve(
341
- parseClaudeResult(Buffer.concat(chunks).toString("utf8"), request)
342
- );
445
+ resolve(parseClaudeResult(stdout, request));
343
446
  } catch {
344
447
  reject(new Error("claude_runs_output_rejected"));
345
448
  }
@@ -353,39 +456,100 @@ export function startClaudeRun(
353
456
  export function createClaudeRunsApi({
354
457
  token,
355
458
  connection,
356
- execute = (request) => {
459
+ execute = null,
460
+ audit = () => {},
461
+ maxConcurrency = CLAUDE_RUNS_MAX_CONCURRENCY,
462
+ // Tool lanes (replies, follow-ups) have their own slots so a batch of
463
+ // openers can never crowd them out.
464
+ maxToolConcurrency = CLAUDE_RUNS_MAX_CONCURRENCY,
465
+ toolLanes = null,
466
+ checkAuthentication = (configDir) =>
467
+ readClaudeAuthentication({ root: connection.localRoot, configDir }),
468
+ accounts = () => claudeAccountDirs(connection.localRoot),
469
+ startRun = startClaudeRun,
470
+ now = Date.now,
471
+ } = {}) {
472
+ if (!/^[a-f0-9]{64}$/.test(token ?? ""))
473
+ throw new Error("claude_runs_key_rejected");
474
+ // An account whose plan is used up rests until its reset; runs go to the
475
+ // next signed-in account meanwhile, and back to the first once it resets.
476
+ const usageLimitedUntil = new Map();
477
+ const resting = (configDir) =>
478
+ (usageLimitedUntil.get(configDir) ?? 0) > now();
479
+ const runOnAccounts = (request) => {
357
480
  let activeRun;
358
481
  let stopped = false;
359
482
  return {
360
483
  promise: (async () => {
361
- const state = await checkAuthentication();
362
- if (stopped) throw new Error("claude_runs_cancelled");
363
- if (state !== "READY")
364
- throw new Error(
365
- state === "SIGN_IN_REQUIRED"
366
- ? "claude_runs_sign_in_required"
367
- : "claude_runs_auth_check_unavailable"
368
- );
369
- activeRun = startClaudeRun(request, { root: connection.localRoot });
370
- return activeRun.promise;
484
+ let failure = null;
485
+ for (const configDir of accounts()) {
486
+ if (resting(configDir)) {
487
+ failure = "claude_runs_usage_limited";
488
+ continue;
489
+ }
490
+ const state = await checkAuthentication(configDir);
491
+ if (stopped) throw new Error("claude_runs_cancelled");
492
+ if (state !== "READY") {
493
+ failure ??=
494
+ state === "SIGN_IN_REQUIRED"
495
+ ? "claude_runs_sign_in_required"
496
+ : "claude_runs_auth_check_unavailable";
497
+ continue;
498
+ }
499
+ activeRun = startRun(request, {
500
+ root: connection.localRoot,
501
+ configDir,
502
+ });
503
+ try {
504
+ return await activeRun.promise;
505
+ } catch (error) {
506
+ if (stopped || error.message !== "claude_runs_usage_limited")
507
+ throw error;
508
+ usageLimitedUntil.set(configDir, error.retryAt);
509
+ failure = "claude_runs_usage_limited";
510
+ }
511
+ }
512
+ throw new Error(failure ?? "claude_runs_sign_in_required");
371
513
  })(),
372
514
  stop() {
373
515
  stopped = true;
374
516
  activeRun?.stop();
375
517
  },
376
518
  };
377
- },
378
- audit = () => {},
379
- maxConcurrency = CLAUDE_RUNS_MAX_CONCURRENCY,
380
- // Tool lanes (replies, follow-ups) have their own slots so a batch of
381
- // openers can never crowd them out.
382
- maxToolConcurrency = CLAUDE_RUNS_MAX_CONCURRENCY,
383
- toolLanes = null,
384
- checkAuthentication = () =>
385
- readClaudeAuthentication({ root: connection.localRoot }),
386
- } = {}) {
387
- if (!/^[a-f0-9]{64}$/.test(token ?? ""))
388
- throw new Error("claude_runs_key_rejected");
519
+ };
520
+ const executeRun = execute ?? runOnAccounts;
521
+ const readHealth = async () => {
522
+ const dirs = accounts();
523
+ let ready = 0;
524
+ let signedOut = 0;
525
+ const retryAts = [];
526
+ for (const configDir of dirs) {
527
+ if (resting(configDir)) {
528
+ retryAts.push(usageLimitedUntil.get(configDir));
529
+ continue;
530
+ }
531
+ const state = await checkAuthentication(configDir).catch(
532
+ () => "UNAVAILABLE"
533
+ );
534
+ if (state === "READY") ready++;
535
+ if (state === "SIGN_IN_REQUIRED") signedOut++;
536
+ }
537
+ return {
538
+ authentication: ready
539
+ ? "READY"
540
+ : signedOut === dirs.length
541
+ ? "SIGN_IN_REQUIRED"
542
+ : "UNAVAILABLE",
543
+ accounts: {
544
+ total: dirs.length,
545
+ ready,
546
+ usageLimited: retryAts.length,
547
+ retryAt: retryAts.length
548
+ ? new Date(Math.min(...retryAts)).toISOString()
549
+ : null,
550
+ },
551
+ };
552
+ };
389
553
  // The run's own tools and the profile's MCP server, or null for a tool-free run.
390
554
  const resolveTools = (request) => {
391
555
  if (!connection.toolEndpointIds || !toolLanes) return null;
@@ -465,12 +629,11 @@ export function createClaudeRunsApi({
465
629
  )
466
630
  return json(res, 403, { error: "claude_runs_scope_rejected" });
467
631
  if (req.method === "GET" && req.url === "/health") {
468
- const authentication = await checkAuthentication().catch(
469
- () => "UNAVAILABLE"
470
- );
632
+ const { authentication, accounts: accountHealth } = await readHealth();
471
633
  return json(res, 200, {
472
634
  ok: authentication === "READY",
473
635
  authentication,
636
+ accounts: accountHealth,
474
637
  provider: "claude-code",
475
638
  active: inFlight.plain + inFlight.tools,
476
639
  toolActive: inFlight.tools,
@@ -521,7 +684,7 @@ export function createClaudeRunsApi({
521
684
  inFlight[pool]++;
522
685
  runs.set(run.run_id, run);
523
686
  try {
524
- run.execution = execute(request);
687
+ run.execution = executeRun(request);
525
688
  run.execution.promise
526
689
  .then(
527
690
  (result) =>
@@ -542,6 +705,7 @@ export function createClaudeRunsApi({
542
705
  error: [
543
706
  "claude_runs_sign_in_required",
544
707
  "claude_runs_auth_check_unavailable",
708
+ "claude_runs_usage_limited",
545
709
  ].includes(error.message)
546
710
  ? error.message
547
711
  : "claude_runs_generation_failed",
@@ -126,7 +126,7 @@ const RUNTIME_GENERATION = Number(
126
126
  const BOOT_SESSION_ID = randomUUID();
127
127
  const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.21.5";
128
128
  const INSTALLER_PACKAGE =
129
- process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.798";
129
+ process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.799";
130
130
  const MCP_PACKAGE =
131
131
  process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.957";
132
132
  export const ENDPOINT_MAX_CONCURRENT_RUNS = 10;
@@ -38,7 +38,7 @@ import { installAgentHostServices } from "./service-installer.mjs";
38
38
  const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
39
39
  const VERSION = "sellable-agent-host-bootstrap/v1";
40
40
  const RECEIPT_VERSION = "sellable-agent-host-registration/v1";
41
- const INSTALLER_PACKAGE = "@sellable/install@0.1.798";
41
+ const INSTALLER_PACKAGE = "@sellable/install@0.1.799";
42
42
  const MCP_PACKAGE = "@sellable/mcp@0.1.957";
43
43
  const HERMES_VERSION = "0.18.0";
44
44
  const SHA256 = /^[a-f0-9]{64}$/;
@@ -1546,7 +1546,7 @@ export function compileClaimToProfileDesired(activeClaim, config) {
1546
1546
  ]) ||
1547
1547
  pinned.hermesCli !== "hermes" ||
1548
1548
  pinned.hermesVersion !== "0.18.0" ||
1549
- pinned.installerPackage !== "@sellable/install@0.1.798" ||
1549
+ pinned.installerPackage !== "@sellable/install@0.1.799" ||
1550
1550
  pinned.mcpPackage !== "@sellable/mcp@0.1.957" ||
1551
1551
  !Array.isArray(policy.toolInclude) ||
1552
1552
  policy.toolInclude.length === 0 ||
@@ -312,7 +312,7 @@ function validateDesired(desired) {
312
312
  desired.serviceCredentialGeneration < 1 ||
313
313
  desired.hermesCli !== "hermes" ||
314
314
  desired.hermesVersion !== "0.18.0" ||
315
- desired.installerPackage !== "@sellable/install@0.1.798" ||
315
+ desired.installerPackage !== "@sellable/install@0.1.799" ||
316
316
  desired.mcpPackage !== "@sellable/mcp@0.1.957" ||
317
317
  !Array.isArray(desired.toolInclude) ||
318
318
  desired.toolInclude.length === 0 ||
@@ -25,7 +25,7 @@ import { fileURLToPath } from "node:url";
25
25
  import { deriveContainedProfileId } from "./profile-materializer.mjs";
26
26
 
27
27
  export const PROVISIONING_ACTION = "PROVISION_HERMES_PROFILE";
28
- export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.798";
28
+ export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.799";
29
29
  export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.957";
30
30
  const PINNED_INSTALL_VERSION = PINNED_INSTALL_PACKAGE.slice(
31
31
  PINNED_INSTALL_PACKAGE.lastIndexOf("@") + 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.798",
3
+ "version": "0.1.799",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code, Codex, and Hermes",
6
6
  "bin": {