@kanonak-protocol/cli 5.4.0 → 5.6.0

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.
@@ -18,6 +18,9 @@ import type { FetchLike } from './IssuanceClient.js';
18
18
  /** The discovery capability the flow needs (satisfied by {@link OAuthDiscovery}). */
19
19
  export interface EnrollmentMetadataSource {
20
20
  discover(host: string): Promise<OAuthServerMetadata | null>;
21
+ /** Resolve a publisher to its authority issuer URL(s) via the always-public
22
+ * RFC 9728 `oauth-protected-resource` document. */
23
+ discoverProtectedResource(host: string): Promise<string[] | null>;
21
24
  }
22
25
  /** Just enough of {@link CredentialStore} to reuse a stored OAuth client_id. */
23
26
  export interface CredentialLookup {
@@ -58,7 +61,7 @@ export declare class EnrollmentFlow {
58
61
  private readonly fetchImpl;
59
62
  private readonly openBrowser;
60
63
  constructor(deps?: EnrollmentFlowDeps);
61
- enroll(host: string, opts: EnrollOptions): Promise<EnrollmentResult>;
64
+ enroll(publisher: string, opts: EnrollOptions): Promise<EnrollmentResult>;
62
65
  /** A stored OAuth client_id for the host, or a freshly dynamically-registered one. */
63
66
  private resolveClientId;
64
67
  /** Exchange an authorization code (PKCE) for the short-lived consent token. */
@@ -63,10 +63,15 @@ export declare class GenericCapabilityHandler {
63
63
  */
64
64
  private deployFromDoc;
65
65
  /**
66
- * On a "package not found" error, scan the publisher index for
67
- * approximate name matches and print up to 5 suggestions. Helps
68
- * users recover from typos and from passing a 3-segment URI to
69
- * `add` against an old CLI that lacked instance support.
66
+ * A publisher's OWN packages as pinned by its served `kanonak.lock` (keys
67
+ * under `<publisher>/`). The lock-derived package list there is no index
68
+ * enumeration. Empty when the publisher serves no lock.
69
+ */
70
+ private lockedOwnPackages;
71
+ /**
72
+ * On a "package not found" error, scan the publisher's pinned packages for
73
+ * approximate name matches and print up to 5 suggestions. Helps users recover
74
+ * from typos and from passing a 3-segment URI to `add`.
70
75
  */
71
76
  private printPackageSuggestions;
72
77
  remove(capability: LoadedCapability, name: string, options: Record<string, string>): Promise<void>;
@@ -0,0 +1,72 @@
1
+ import type { LockFile, LockEntry, PublisherConfig, DeviceEnrollmentRecord, SessionRecord, AuthenticatedFetchFn } from '@kanonak-protocol/sdk';
2
+ /** The outcome of a connect, for a host (CLI / VS Code) to report. */
3
+ export interface ConnectResult {
4
+ connected: boolean;
5
+ enrolled: boolean;
6
+ installed: number;
7
+ /** A failure reason, or the sentinel `'aborted'` when the user declined the install. */
8
+ error?: string;
9
+ }
10
+ /**
11
+ * Injectable collaborators so `connect` runs identically across surfaces (CLI,
12
+ * VS Code) and is testable without network, a device, or stdin. Every field
13
+ * defaults to the real implementation.
14
+ */
15
+ export interface ConnectDeps {
16
+ fetchFn?: AuthenticatedFetchFn;
17
+ /** Resolve a publisher to its authority via RFC 9728 oauth-protected-resource. */
18
+ discovery?: {
19
+ discoverProtectedResource(host: string): Promise<string[] | null>;
20
+ };
21
+ configResolver?: {
22
+ getConfig(host: string): Promise<PublisherConfig>;
23
+ };
24
+ deviceStore?: {
25
+ get(host: string): Promise<DeviceEnrollmentRecord | null>;
26
+ };
27
+ publisherIndex?: {
28
+ getLock(host: string): Promise<LockFile | null>;
29
+ };
30
+ sessionManager?: {
31
+ getValidSession(host: string): Promise<SessionRecord | null>;
32
+ };
33
+ /** Run device enrollment for a publisher (resolves its authority internally). */
34
+ enroll?: (publisher: string) => Promise<{
35
+ success: boolean;
36
+ thumbprint?: string;
37
+ error?: string;
38
+ }>;
39
+ /** Fetch + verify + cache one pinned package. */
40
+ installEntry?: (publisher: string, packageName: string, entry: LockEntry) => Promise<void>;
41
+ loadLock?: () => LockFile | null;
42
+ saveLock?: (lock: LockFile) => void;
43
+ /** Ask the user to confirm the install. The host supplies its own prompt. */
44
+ confirm?: (question: string) => Promise<boolean>;
45
+ /** Progress output sink. */
46
+ log?: (message: string) => void;
47
+ }
48
+ /**
49
+ * The `connect` orchestrator core (issue #9): given a publisher domain, learn
50
+ * whether it is gated from the always-public RFC 9728 `oauth-protected-resource`
51
+ * document, enroll this device if it is gated and not already enrolled, then
52
+ * read the publisher's `kanonak.json` (its `featured_packages`) and install them
53
+ * and their pinned closures from the served `kanonak.lock`.
54
+ *
55
+ * For a gated publisher the `kanonak.json`/`kanonak.lock` are themselves behind
56
+ * auth, so those reads go through the device session minted from the enrolled
57
+ * cert. A public publisher (no `oauth-protected-resource`) never enrolls and
58
+ * reads everything anonymously. Pure of process/stdout concerns — returns a
59
+ * {@link ConnectResult} and reports via `deps.log`.
60
+ */
61
+ export declare function connect(publisher: string, options: {
62
+ install?: boolean;
63
+ yes?: boolean;
64
+ }, deps?: ConnectDeps): Promise<ConnectResult>;
65
+ /**
66
+ * `kanonak connect <publisher>` — the CLI wrapper: console output, a stdin
67
+ * confirmation prompt, and a non-zero exit on failure.
68
+ */
69
+ export declare function connectCommand(publisher: string, options: {
70
+ install?: boolean;
71
+ yes?: boolean;
72
+ }): Promise<void>;
@@ -1,8 +1,33 @@
1
+ import { KanonakParser } from '@kanonak-protocol/sdk';
2
+ import type { AuthenticatedFetchFn } from '@kanonak-protocol/sdk';
3
+ import type { LockFile, LockEntry } from '@kanonak-protocol/sdk';
4
+ import { FileCache } from '../cache/FileCache.js';
1
5
  /**
2
6
  * Install a package and its transitive dependencies to the local cache.
3
7
  *
4
- * Usage: kanonak install kanonak.org/core-rdf@1.0.1
5
- * kanonak install kanonak.org/core-rdf (resolves latest compatible)
6
- * kanonak install (install from kanonak.lock)
8
+ * Resolution flows through the publisher's SERVED `kanonak.lock` — the
9
+ * authoritative, integrity-verified closure (one pinned version per package).
10
+ * Installing `publisher/package` installs exactly the version that lock pins
11
+ * plus the closure it pins, verifying every fetched file against the lock's
12
+ * sha256 before caching (the `npm ci` model). There is no version-range
13
+ * selection: the publisher's lock IS the resolution.
14
+ *
15
+ * Usage: kanonak install kanonak.org/core-rdf (install what the lock pins)
16
+ * kanonak install kanonak.org/core-rdf@1.0.1 (must match the pinned version)
17
+ * kanonak install (install from the local kanonak.lock)
7
18
  */
8
19
  export declare function installCommand(packageRef?: string): Promise<void>;
20
+ /**
21
+ * Ensure a pinned package is in the cache: a cache hit is verified against the
22
+ * lock's integrity; a miss is fetched from the entry's `resolved` URL, verified,
23
+ * identity-checked, then cached. Never caches bytes whose hash or namespace
24
+ * disagrees with the lock — a mismatch throws.
25
+ */
26
+ export declare function fetchVerifyCache(publisher: string, packageName: string, entry: LockEntry, fileCache: FileCache, parser: KanonakParser, fetchFn: AuthenticatedFetchFn): Promise<void>;
27
+ /**
28
+ * The keys of `rootKey` and every package reachable through its (transitive)
29
+ * `dependencies`, all resolved within `lock`. A dependency the lock references
30
+ * but does not pin is a broken lock — throw rather than silently install a
31
+ * partial closure.
32
+ */
33
+ export declare function collectClosure(rootKey: string, lock: LockFile): string[];