@sellable/install 0.1.471 → 0.1.472

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.
@@ -53,8 +53,10 @@ import {
53
53
  exchangeFlyAdminIdentity,
54
54
  flyRuntimeDigest,
55
55
  handoffFlyOidcSocket,
56
+ isFlyCustomerEnrollmentRenewalError,
56
57
  loadOrCreateFlyRuntimeIdentity,
57
58
  obtainFlyOidcToken,
59
+ renewFlyAdminControlSession,
58
60
  signedWorkerAuthorization,
59
61
  } from "../fly-runtime-identity.mjs";
60
62
  import {
@@ -2263,7 +2265,7 @@ async function runAdminProduction() {
2263
2265
  identityRoot: join(RUNTIME_ROOT, "identity"),
2264
2266
  persistent: true,
2265
2267
  });
2266
- const exchange = await exchangeFlyAdminIdentity({
2268
+ let exchange = await exchangeFlyAdminIdentity({
2267
2269
  origin: ORIGIN,
2268
2270
  runtimeId: RUNTIME_ID,
2269
2271
  audience: AUDIENCE,
@@ -2280,7 +2282,7 @@ async function runAdminProduction() {
2280
2282
  }
2281
2283
  const deliveryKey = loadOrCreateDeliveryKey();
2282
2284
  await enrollDeliveryKey({ identity, exchange, deliveryKey });
2283
- const client = createFlyCustomerControlClient({
2285
+ let client = createFlyCustomerControlClient({
2284
2286
  origin: ORIGIN,
2285
2287
  identity,
2286
2288
  exchange,
@@ -2321,7 +2323,35 @@ async function runAdminProduction() {
2321
2323
  let failures = 0;
2322
2324
  while (!controller.signal.aborted) {
2323
2325
  try {
2324
- const claim = await client.claim(60);
2326
+ let claim;
2327
+ try {
2328
+ claim = await client.claim(60);
2329
+ } catch (error) {
2330
+ if (!isFlyCustomerEnrollmentRenewalError(error)) throw error;
2331
+ const renewed = await renewFlyAdminControlSession({
2332
+ origin: ORIGIN,
2333
+ runtimeId: RUNTIME_ID,
2334
+ audience: AUDIENCE,
2335
+ identity,
2336
+ bootSessionId: BOOT_SESSION_ID,
2337
+ runtimeGeneration: RUNTIME_GENERATION,
2338
+ adminRuntimeGenerationId:
2339
+ exchange.adminDeliveryTarget.adminRuntimeGenerationId,
2340
+ adminRuntimeGenerationFence:
2341
+ exchange.adminDeliveryTarget.adminRuntimeGenerationFence,
2342
+ });
2343
+ exchange = renewed.exchange;
2344
+ client = renewed.client;
2345
+ failures = 0;
2346
+ process.stderr.write(
2347
+ `${JSON.stringify({
2348
+ ok: true,
2349
+ code: "admin_enrollment_renewed",
2350
+ keyGeneration: exchange.keyGeneration,
2351
+ })}\n`
2352
+ );
2353
+ continue;
2354
+ }
2325
2355
  if (controller.signal.aborted) break;
2326
2356
  if (!claim) {
2327
2357
  if (activeLaunch) {
@@ -998,3 +998,65 @@ export async function renewFlyCustomerControlSession({
998
998
  });
999
999
  return Object.freeze({ exchange, client });
1000
1000
  }
1001
+
1002
+ export async function renewFlyAdminControlSession({
1003
+ origin,
1004
+ runtimeId,
1005
+ audience,
1006
+ identity,
1007
+ bootSessionId,
1008
+ runtimeGeneration,
1009
+ adminRuntimeGenerationId,
1010
+ adminRuntimeGenerationFence,
1011
+ obtainOidcTokenImpl = obtainFlyOidcToken,
1012
+ exchangeFlyAdminIdentityImpl = exchangeFlyAdminIdentity,
1013
+ createFlyCustomerControlClientImpl = createFlyCustomerControlClient,
1014
+ }) {
1015
+ if (
1016
+ !Number.isSafeInteger(runtimeGeneration) ||
1017
+ runtimeGeneration < 1 ||
1018
+ !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(
1019
+ bootSessionId ?? ""
1020
+ ) ||
1021
+ !SAFE_ID.test(adminRuntimeGenerationId ?? "") ||
1022
+ !/^[1-9][0-9]{0,19}$/.test(adminRuntimeGenerationFence ?? "") ||
1023
+ typeof obtainOidcTokenImpl !== "function" ||
1024
+ typeof exchangeFlyAdminIdentityImpl !== "function" ||
1025
+ typeof createFlyCustomerControlClientImpl !== "function"
1026
+ ) {
1027
+ throw new Error("admin_runtime_generation_rejected");
1028
+ }
1029
+ let oidcToken = await obtainOidcTokenImpl({ audience });
1030
+ let exchange;
1031
+ try {
1032
+ exchange = await exchangeFlyAdminIdentityImpl({
1033
+ origin,
1034
+ runtimeId,
1035
+ audience,
1036
+ identity,
1037
+ bootSessionId,
1038
+ oidcToken,
1039
+ });
1040
+ } finally {
1041
+ oidcToken = null;
1042
+ }
1043
+ if (
1044
+ exchange?.kind !== "ADMIN" ||
1045
+ exchange.runtimeId !== runtimeId ||
1046
+ exchange.publicKeyHash !== identity?.publicKeyHash ||
1047
+ exchange.keyGeneration !== runtimeGeneration ||
1048
+ exchange.adminDeliveryTarget?.runtimeGeneration !== runtimeGeneration ||
1049
+ exchange.adminDeliveryTarget.adminRuntimeGenerationId !==
1050
+ adminRuntimeGenerationId ||
1051
+ exchange.adminDeliveryTarget.adminRuntimeGenerationFence !==
1052
+ adminRuntimeGenerationFence
1053
+ ) {
1054
+ throw new Error("admin_runtime_generation_rejected");
1055
+ }
1056
+ const client = createFlyCustomerControlClientImpl({
1057
+ origin,
1058
+ identity,
1059
+ exchange,
1060
+ });
1061
+ return Object.freeze({ exchange, client });
1062
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/install",
3
- "version": "0.1.471",
3
+ "version": "0.1.472",
4
4
  "type": "module",
5
5
  "description": "One-command installer for Sellable MCP in Claude Code, Codex, and Hermes",
6
6
  "bin": {
@@ -18,6 +18,7 @@ allowed-tools:
18
18
  - mcp__sellable__search_subskill_prompts
19
19
  - mcp__sellable__list_senders
20
20
  - mcp__sellable__select_campaign_cells
21
+ - mcp__sellable__wait_for_campaign_processing
21
22
  ---
22
23
 
23
24
  # Refill Sends
@@ -591,8 +592,16 @@ asset is the contract to load — load the prompt and that asset, verify the `v3
591
592
  compatible range, and report the version before the first call. Call
592
593
  `refill_v3_advance` with `{workspaceId, scope}` — plus `yolo: true` only when the
593
594
  operator asked for `--yolo` — and call it again after each `advanced` or
594
- `campaign_attention_required` result until it answers `awaiting_external_change`,
595
- `complete`, or `blocked`.
595
+ `campaign_attention_required` result. An `awaiting_external_change` result with
596
+ `deferral.code:"campaign_work_in_flight"` and a campaign id is a bounded
597
+ read-only continuation, not permission to walk the next campaign: call
598
+ `wait_for_campaign_processing({ workspaceId, campaignId, requireIdle:true })`,
599
+ then call `refill_v3_advance` again with the identical scope and yolo grant.
600
+ This reuses Create Campaign's campaign-table waiter and guarantees the same
601
+ campaign is freshly replanned after its cohort settles. If the bounded wait
602
+ returns a partial timeout, surface that checkpoint and make the fresh advance;
603
+ if it reports the same live campaign, repeat the bounded wait. Stop on every
604
+ other `awaiting_external_change`, `complete`, or `blocked` result.
596
605
 
597
606
  Resolve the workspace BEFORE the first call, exactly as the V1 route already
598
607
  requires. `workspaceId` must be an exact id on every automation call: when the