@north-light/cli 0.3.541 → 0.3.543

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.
@@ -1,5 +1,5 @@
1
1
  /** The contract level this build serves and requires. Written by `scripts/contract.mjs release`; never edit by hand. */
2
- export declare const CONTRACT_LEVEL = "1.28";
2
+ export declare const CONTRACT_LEVEL = "1.29";
3
3
  /** Request header naming the one contract level the caller needs, e.g. `1.3`. Optional: absent means no check. */
4
4
  export declare const REQUIRED_CONTRACT_HEADER = "Crouter-Required-Contract";
5
5
  /** Response header listing the contract levels the server serves, comma-separated, one per major, e.g. `1.3`. */
@@ -1 +1 @@
1
- var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.28",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
1
+ var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.29",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
@@ -32,7 +32,7 @@ definePlugin({
32
32
  });
33
33
  ```
34
34
 
35
- Each group has `consentLine`, the line the directory's consent page shows, and optionally `who` (`own`, `user`, `allowlist`, `any` or `team:<id>`; the directory treats an omitted `who` as `user`) and `allowlist` (app principals `app:<id>`, required with `who: 'allowlist'` and refused otherwise). `buildBundle` writes the block into `bundle.json` as `groups`, and the directory applies it when it refreshes the provider's manifest. The directory refuses a provider archive whose `bundle.json` has no `groups` (`bundle.json groups: is required`), and a provider's groups come only from this declaration: the directory's console and CLI do not edit groups. `definePlugin` itself accepts a provider without `groups`, but once declared every leaf's `group` must name a declared group and every declared group must be used by at least one leaf; `definePlugin` throws `PluginDefinitionError` otherwise. A plugin that is not `provider: true` must not declare `groups`.
35
+ Each group has `consentLine`, the line the directory's consent page shows, and optionally `who` (`own`, `user`, `allowlist`, `any` or `team:<id>`; the directory treats an omitted `who` as `user`) and `allowlist` (app principals `app:<id>`, required with `who: 'allowlist'` and refused otherwise). `buildBundle` writes the block into `bundle.json` as `groups`, and the directory applies it when it refreshes the provider's manifest. The directory refuses a provider archive whose `bundle.json` has no `groups` (`bundle.json groups: is required`), and a provider's groups come only from this declaration: the directory's console and CLI do not edit groups. `definePlugin` itself accepts a provider without `groups`, but once declared every leaf's `group` must name a declared group, or `definePlugin` throws `PluginDefinitionError`. A declared group no leaf uses is allowed: a provider that ships documents only (`commands: {}` and a `memory` array) declares one so a grant naming it installs the bundle, and it adds no `crtr` command. A plugin that is not `provider: true` must not declare `groups`.
36
36
 
37
37
  A branch or leaf may set `tier` to `common` or `important` to be promoted into its parent's `-h`. A command with more than 40 children lists only its promoted children in its own `-h`, followed by the count of the rest; the others still run and answer `-h` by name. Use this when a plugin mounts one command per entry of a large catalog. On such a command only promoted leaves reach the top-level `crtr -h`; a promoted branch shows in the command's own `-h`.
38
38
 
@@ -9,6 +9,14 @@ when-and-why-to-read: When you need Release notes, this knowledge should be read
9
9
 
10
10
  Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
11
11
 
12
+ ## 0.3.542
13
+
14
+ The directory's account page reads and removes individual AI subscription accounts. Additive: no app-facing request or response changes.
15
+
16
+ - `runtime.vendor-status` (directory token) carries an optional `accounts` field: per vendor, each signed-in account's `id`, `label`, `state` (`connected`, `needs_reconnecting`, `rate_limited`) and, when rate limited, `cooling_until`.
17
+ - New `runtime.vendor-account-remove` (`POST /v1/runtime/vendor-account-remove`, directory token with purpose `vendor_account_remove`, body `{ vendor, account_id }`).
18
+ - A refresh token the vendor rejects (`invalid_grant`) on the daemon's model route now flags the account as needing reconnecting, as it already did on a node's own turns.
19
+
12
20
  ## 0.3.539
13
21
 
14
22
  `exchangeIdentity` now returns the verified identity when the directory's token answer also carries a runtime connection. It used to throw `oauth_exchange_mismatch`, so no member could sign in to an app held by a team with a runtime grant (the directory gives that app a connection on every sign-in). The SDK revokes the unneeded refresh token best-effort; revocation removes only that token, never the grant. `exchangeCode` still refuses a sign-in-only answer.
@@ -1,7 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { createAssistantMessageEventStream, } from '@earendil-works/pi-ai';
3
3
  import { anthropicMessagesApi, openAICodexResponsesApi, openAIResponsesApi } from '@earendil-works/pi-ai/compat';
4
- import { ANTHROPIC_PROVIDER_ID, OPENAI_CHATGPT_PROVIDER_ID, OPENAI_CODEX_PROVIDER_ID, INVALID_REFRESH_TOKEN_BACKOFF_MS, openAIDeviceId, MAX_RATE_LIMIT_COOLDOWN_MS, coolingUntilFor, formatCooldowns, formatStatusLine, getProviderLabel, isManagedProvider, isInvalidRefreshTokenError, addSubscription, enrichManagedLoginIdentity, markSubscriptionAuthenticationFailed, markSubscriptionRateLimited, markSubscriptionSuccess, retryAfterMsFromHeaders, retryAfterMsFromError, promoteSubscription, readRotationConfig, readSubscriptionPool, refreshSubscriptionCredential, removeManagedAccount, resolveProviderCandidates, upsertSubscriptionWithUniqueAccount, subscriptionNeedsSuccessWrite, } from '../../../core/subscription-state.js';
4
+ import { ANTHROPIC_PROVIDER_ID, OPENAI_CHATGPT_PROVIDER_ID, OPENAI_CODEX_PROVIDER_ID, openAIDeviceId, MAX_RATE_LIMIT_COOLDOWN_MS, coolingUntilFor, formatCooldowns, formatStatusLine, getProviderLabel, isManagedProvider, isInvalidRefreshTokenError, addSubscription, enrichManagedLoginIdentity, markSubscriptionRateLimited, markSubscriptionSuccess, retryAfterMsFromHeaders, retryAfterMsFromError, promoteSubscription, readRotationConfig, readSubscriptionPool, refreshSubscriptionCredential, removeManagedAccount, resolveProviderCandidates, upsertSubscriptionWithUniqueAccount, subscriptionNeedsSuccessWrite, } from '../../../core/subscription-state.js';
5
5
  import { classifyRateLimit, describeRateLimitScope, rateLimitHeaders } from '../../../core/rate-limit-scope.js';
6
6
  import { readMergedLaunchConfig } from '../../../core/config.js';
7
7
  import { expandModelCandidates, modelRequestFromConfig, probeRouteAvailability } from '../../../core/model-routes.js';
@@ -732,8 +732,6 @@ async function runManagedProvider(state, model, context, options, stream) {
732
732
  state.runtimeContext?.ui.notify(`${getProviderLabel(providerId)} re-authentication failed: ${loginError instanceof Error ? loginError.message : String(loginError)}`, 'error');
733
733
  }
734
734
  if (!reauthenticated) {
735
- const attemptAt = Date.now();
736
- await recordSubscriptionMutation(`recording ${getProviderLabel(providerId)} authentication failure`, () => markSubscriptionAuthenticationFailed(providerId, credential.label, credential, INVALID_REFRESH_TOKEN_BACKOFF_MS, attemptAt));
737
735
  authFailures.add(`${providerId}/${credential.label}`);
738
736
  continue;
739
737
  }
@@ -17,7 +17,6 @@ import {
17
17
  ANTHROPIC_PROVIDER_ID,
18
18
  OPENAI_CHATGPT_PROVIDER_ID,
19
19
  OPENAI_CODEX_PROVIDER_ID,
20
- INVALID_REFRESH_TOKEN_BACKOFF_MS,
21
20
  openAIDeviceId,
22
21
  MAX_RATE_LIMIT_COOLDOWN_MS,
23
22
  coolingUntilFor,
@@ -28,7 +27,6 @@ import {
28
27
  isInvalidRefreshTokenError,
29
28
  addSubscription,
30
29
  enrichManagedLoginIdentity,
31
- markSubscriptionAuthenticationFailed,
32
30
  markSubscriptionRateLimited,
33
31
  markSubscriptionSuccess,
34
32
  retryAfterMsFromHeaders,
@@ -940,8 +938,6 @@ async function runManagedProvider(
940
938
  state.runtimeContext?.ui.notify(`${getProviderLabel(providerId)} re-authentication failed: ${loginError instanceof Error ? loginError.message : String(loginError)}`, 'error');
941
939
  }
942
940
  if (!reauthenticated) {
943
- const attemptAt = Date.now();
944
- await recordSubscriptionMutation(`recording ${getProviderLabel(providerId)} authentication failure`, () => markSubscriptionAuthenticationFailed(providerId, credential.label, credential, INVALID_REFRESH_TOKEN_BACKOFF_MS, attemptAt));
945
941
  authFailures.add(`${providerId}/${credential.label}`);
946
942
  continue;
947
943
  }