@pellux/goodvibes-sdk 0.18.48 → 0.18.50

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 (59) hide show
  1. package/README.md +15 -13
  2. package/dist/_internal/errors/index.d.ts +111 -0
  3. package/dist/_internal/errors/index.d.ts.map +1 -1
  4. package/dist/_internal/errors/index.js +128 -0
  5. package/dist/_internal/platform/auth/index.d.ts +6 -0
  6. package/dist/_internal/platform/auth/index.d.ts.map +1 -0
  7. package/dist/_internal/platform/auth/index.js +4 -0
  8. package/dist/_internal/platform/auth/oauth-client.d.ts +45 -0
  9. package/dist/_internal/platform/auth/oauth-client.d.ts.map +1 -0
  10. package/dist/_internal/platform/auth/oauth-client.js +45 -0
  11. package/dist/_internal/platform/auth/permission-resolver.d.ts +35 -0
  12. package/dist/_internal/platform/auth/permission-resolver.d.ts.map +1 -0
  13. package/dist/_internal/platform/auth/permission-resolver.js +57 -0
  14. package/dist/_internal/platform/auth/session-manager.d.ts +31 -0
  15. package/dist/_internal/platform/auth/session-manager.d.ts.map +1 -0
  16. package/dist/_internal/platform/auth/session-manager.js +44 -0
  17. package/dist/_internal/platform/auth/token-store.d.ts +23 -0
  18. package/dist/_internal/platform/auth/token-store.d.ts.map +1 -0
  19. package/dist/_internal/platform/auth/token-store.js +34 -0
  20. package/dist/_internal/platform/core/orchestrator.d.ts +75 -11
  21. package/dist/_internal/platform/core/orchestrator.d.ts.map +1 -1
  22. package/dist/_internal/platform/core/orchestrator.js +29 -8
  23. package/dist/_internal/platform/version.js +1 -1
  24. package/dist/_internal/transport-http/http-core.d.ts +2 -0
  25. package/dist/_internal/transport-http/http-core.d.ts.map +1 -1
  26. package/dist/_internal/transport-http/http-core.js +29 -2
  27. package/dist/_internal/transport-http/http.d.ts.map +1 -1
  28. package/dist/_internal/transport-http/http.js +50 -2
  29. package/dist/_internal/transport-realtime/domain-events.d.ts +26 -0
  30. package/dist/_internal/transport-realtime/domain-events.d.ts.map +1 -1
  31. package/dist/_internal/transport-realtime/domain-events.js +63 -0
  32. package/dist/_internal/transport-realtime/index.d.ts +2 -2
  33. package/dist/_internal/transport-realtime/index.d.ts.map +1 -1
  34. package/dist/_internal/transport-realtime/index.js +2 -2
  35. package/dist/_internal/transport-realtime/runtime-events.d.ts +27 -1
  36. package/dist/_internal/transport-realtime/runtime-events.d.ts.map +1 -1
  37. package/dist/_internal/transport-realtime/runtime-events.js +27 -0
  38. package/dist/auth.d.ts +77 -0
  39. package/dist/auth.d.ts.map +1 -1
  40. package/dist/auth.js +52 -0
  41. package/dist/browser.d.ts +24 -0
  42. package/dist/browser.d.ts.map +1 -1
  43. package/dist/browser.js +24 -0
  44. package/dist/client.d.ts +158 -0
  45. package/dist/client.d.ts.map +1 -1
  46. package/dist/client.js +22 -0
  47. package/dist/expo.d.ts +15 -0
  48. package/dist/expo.d.ts.map +1 -1
  49. package/dist/expo.js +15 -0
  50. package/dist/node.d.ts +23 -0
  51. package/dist/node.d.ts.map +1 -1
  52. package/dist/node.js +23 -0
  53. package/dist/react-native.d.ts +27 -0
  54. package/dist/react-native.d.ts.map +1 -1
  55. package/dist/react-native.js +27 -0
  56. package/dist/web.d.ts +13 -0
  57. package/dist/web.d.ts.map +1 -1
  58. package/dist/web.js +13 -0
  59. package/package.json +1 -1
package/README.md CHANGED
@@ -2,34 +2,36 @@
2
2
 
3
3
  Umbrella GoodVibes SDK with Node, browser, web UI, React Native, and Expo integration helpers.
4
4
 
5
+ > **What this SDK is:** a client for the GoodVibes daemon. Not a direct provider SDK.
6
+ > See [Getting Started](../../docs/getting-started.md) for the full walkthrough.
7
+
5
8
  Install:
6
9
 
7
10
  ```bash
8
11
  npm install @pellux/goodvibes-sdk
9
12
  ```
10
13
 
11
- This is one package with subpath exports.
12
-
13
- Entry points:
14
- - `@pellux/goodvibes-sdk`
15
- - `@pellux/goodvibes-sdk/auth`
16
- - `@pellux/goodvibes-sdk/node`
17
- - `@pellux/goodvibes-sdk/browser`
18
- - `@pellux/goodvibes-sdk/web`
19
- - `@pellux/goodvibes-sdk/react-native`
20
- - `@pellux/goodvibes-sdk/expo`
21
-
22
- Example:
14
+ Quick example (Node / Bun):
23
15
 
24
16
  ```ts
25
17
  import { createNodeGoodVibesSdk } from '@pellux/goodvibes-sdk/node';
18
+ import { createMemoryTokenStore } from '@pellux/goodvibes-sdk/auth';
26
19
 
27
20
  const sdk = createNodeGoodVibesSdk({
28
21
  baseUrl: 'http://127.0.0.1:3210',
29
- authToken: process.env.GOODVIBES_TOKEN ?? null,
22
+ tokenStore: createMemoryTokenStore(process.env.GOODVIBES_TOKEN ?? null),
30
23
  });
31
24
 
32
25
  console.log(await sdk.operator.control.snapshot());
33
26
  ```
34
27
 
28
+ Entry points:
29
+ - `@pellux/goodvibes-sdk`
30
+ - `@pellux/goodvibes-sdk/auth`
31
+ - `@pellux/goodvibes-sdk/node`
32
+ - `@pellux/goodvibes-sdk/browser`
33
+ - `@pellux/goodvibes-sdk/web`
34
+ - `@pellux/goodvibes-sdk/react-native`
35
+ - `@pellux/goodvibes-sdk/expo`
36
+
35
37
  Use this package when you want the main consumer-facing GoodVibes TypeScript SDK rather than lower-level pieces.
@@ -2,6 +2,20 @@ import type { DaemonErrorCategory, DaemonErrorSource, StructuredDaemonErrorBody
2
2
  export type { DaemonErrorCategory, DaemonErrorSource, StructuredDaemonErrorBody, } from './daemon-error-contract.js';
3
3
  export type ErrorCategory = DaemonErrorCategory | 'contract';
4
4
  export type ErrorSource = DaemonErrorSource | 'contract';
5
+ /**
6
+ * Tagged union discriminant for all SDK errors. Use this for exhaustive
7
+ * switch/if-else handling instead of `instanceof` chains.
8
+ *
9
+ * @example
10
+ * if (error instanceof GoodVibesSdkError) {
11
+ * if (error.kind === 'rate-limit') {
12
+ * await delay(error.retryAfterMs ?? 1000);
13
+ * } else if (error.kind === 'auth') {
14
+ * // refresh credentials
15
+ * }
16
+ * }
17
+ */
18
+ export type SDKErrorKind = 'auth' | 'config' | 'contract' | 'network' | 'not-found' | 'rate-limit' | 'server' | 'validation' | 'unknown';
5
19
  export interface GoodVibesSdkErrorOptions {
6
20
  readonly code?: string;
7
21
  readonly category?: ErrorCategory;
@@ -21,7 +35,31 @@ export interface GoodVibesSdkErrorOptions {
21
35
  readonly retryAfterMs?: number;
22
36
  }
23
37
  export declare const RETRYABLE_STATUS_CODES: readonly number[];
38
+ /**
39
+ * Base error class for all errors thrown by the GoodVibes SDK.
40
+ *
41
+ * Every error carries a structured `category` and `source` that allow
42
+ * callers to handle specific failure modes without string-matching messages.
43
+ *
44
+ * ### Narrowing pattern
45
+ * ```ts
46
+ * import { GoodVibesSdkError, HttpStatusError, ConfigurationError } from '@pellux/goodvibes-sdk';
47
+ *
48
+ * try {
49
+ * await sdk.operator.agents.list();
50
+ * } catch (err) {
51
+ * if (err instanceof HttpStatusError && err.category === 'rate_limit') {
52
+ * // Back off and retry after err.retryAfterMs
53
+ * } else if (err instanceof ConfigurationError) {
54
+ * // Invalid SDK setup — not recoverable
55
+ * } else if (err instanceof GoodVibesSdkError) {
56
+ * console.error(err.category, err.hint);
57
+ * }
58
+ * }
59
+ * ```
60
+ */
24
61
  export declare class GoodVibesSdkError extends Error {
62
+ readonly kind: SDKErrorKind;
25
63
  readonly code?: string;
26
64
  readonly category: ErrorCategory;
27
65
  readonly source: ErrorSource;
@@ -40,12 +78,85 @@ export declare class GoodVibesSdkError extends Error {
40
78
  readonly retryAfterMs?: number;
41
79
  constructor(message: string, options?: GoodVibesSdkErrorOptions);
42
80
  }
81
+ /**
82
+ * Thrown when the SDK is misconfigured (e.g. missing `baseUrl`, no fetch
83
+ * implementation available, or calling a mutation on a read-only auth resolver).
84
+ *
85
+ * Always non-recoverable (`recoverable: false`).
86
+ * Category: `'config'`. Kind: `'config'`.
87
+ *
88
+ * @deprecated Use `error.kind === 'config'` instead of `instanceof ConfigurationError`.
89
+ * This class is preserved for backward compatibility and still throws normally.
90
+ *
91
+ * @example
92
+ * import { ConfigurationError } from '@pellux/goodvibes-sdk';
93
+ *
94
+ * try {
95
+ * await sdk.auth.setToken('x');
96
+ * } catch (err) {
97
+ * if (err instanceof ConfigurationError) {
98
+ * // SDK was constructed with getAuthToken — token mutation not supported
99
+ * }
100
+ * }
101
+ */
43
102
  export declare class ConfigurationError extends GoodVibesSdkError {
44
103
  constructor(message: string, options?: GoodVibesSdkErrorOptions);
45
104
  }
105
+ /**
106
+ * Thrown when a response from the daemon violates the expected contract
107
+ * (unexpected shape, missing required fields, etc.).
108
+ *
109
+ * Always non-recoverable (`recoverable: false`).
110
+ * Category: `'contract'`. Kind: `'contract'`.
111
+ *
112
+ * @deprecated Use `error.kind === 'contract'` instead of `instanceof ContractError`.
113
+ * This class is preserved for backward compatibility and still throws normally.
114
+ *
115
+ * @example
116
+ * import { ContractError } from '@pellux/goodvibes-sdk';
117
+ *
118
+ * try {
119
+ * const result = await sdk.operator.agents.get({ id: agentId });
120
+ * } catch (err) {
121
+ * if (err instanceof ContractError) {
122
+ * // Daemon returned an unexpected shape — SDK version mismatch?
123
+ * console.error('Contract violation:', err.message);
124
+ * }
125
+ * }
126
+ */
46
127
  export declare class ContractError extends GoodVibesSdkError {
47
128
  constructor(message: string, options?: GoodVibesSdkErrorOptions);
48
129
  }
130
+ /**
131
+ * Thrown when the daemon returns a non-2xx HTTP status code.
132
+ *
133
+ * The `category` field is inferred from the status code:
134
+ * - `401` → `'authentication'`  `402` → `'billing'`  `403` → `'authorization'`
135
+ * - `404` → `'not_found'`  `408` → `'timeout'`  `429` → `'rate_limit'`
136
+ * - `5xx` → `'service'`
137
+ *
138
+ * Use `recoverable` to decide whether to retry, and `retryAfterMs` for
139
+ * the backoff hint on rate-limit responses.
140
+ *
141
+ * @deprecated Use `error.kind` instead of `instanceof HttpStatusError`.
142
+ * For example: `error.kind === 'rate-limit'`, `error.kind === 'auth'`, `error.kind === 'server'`.
143
+ * This class is preserved for backward compatibility and still throws normally.
144
+ *
145
+ * @example
146
+ * import { HttpStatusError } from '@pellux/goodvibes-sdk';
147
+ *
148
+ * try {
149
+ * await sdk.operator.agents.list();
150
+ * } catch (err) {
151
+ * if (err instanceof HttpStatusError) {
152
+ * if (err.category === 'rate_limit') {
153
+ * await delay(err.retryAfterMs ?? 1000);
154
+ * } else if (!err.recoverable) {
155
+ * throw err; // Surface non-retryable errors immediately
156
+ * }
157
+ * }
158
+ * }
159
+ */
49
160
  export declare class HttpStatusError extends GoodVibesSdkError {
50
161
  constructor(message: string, options?: GoodVibesSdkErrorOptions);
51
162
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/_internal/errors/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/_internal/errors/index.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,33 @@
1
+ function inferKind(category) {
2
+ switch (category) {
3
+ case 'authentication':
4
+ case 'authorization':
5
+ case 'billing':
6
+ case 'permission':
7
+ return 'auth';
8
+ case 'config':
9
+ return 'config';
10
+ case 'contract':
11
+ return 'contract';
12
+ case 'network':
13
+ case 'timeout':
14
+ return 'network';
15
+ case 'not_found':
16
+ return 'not-found';
17
+ case 'rate_limit':
18
+ return 'rate-limit';
19
+ case 'protocol':
20
+ case 'service':
21
+ case 'internal':
22
+ return 'server';
23
+ case 'bad_request':
24
+ return 'validation';
25
+ case 'tool':
26
+ case 'unknown':
27
+ default:
28
+ return 'unknown';
29
+ }
30
+ }
1
31
  export const RETRYABLE_STATUS_CODES = [408, 429, 500, 502, 503, 504];
2
32
  function inferCategory(status) {
3
33
  if (status === 400)
@@ -18,7 +48,31 @@ function inferCategory(status) {
18
48
  return 'service';
19
49
  return 'unknown';
20
50
  }
51
+ /**
52
+ * Base error class for all errors thrown by the GoodVibes SDK.
53
+ *
54
+ * Every error carries a structured `category` and `source` that allow
55
+ * callers to handle specific failure modes without string-matching messages.
56
+ *
57
+ * ### Narrowing pattern
58
+ * ```ts
59
+ * import { GoodVibesSdkError, HttpStatusError, ConfigurationError } from '@pellux/goodvibes-sdk';
60
+ *
61
+ * try {
62
+ * await sdk.operator.agents.list();
63
+ * } catch (err) {
64
+ * if (err instanceof HttpStatusError && err.category === 'rate_limit') {
65
+ * // Back off and retry after err.retryAfterMs
66
+ * } else if (err instanceof ConfigurationError) {
67
+ * // Invalid SDK setup — not recoverable
68
+ * } else if (err instanceof GoodVibesSdkError) {
69
+ * console.error(err.category, err.hint);
70
+ * }
71
+ * }
72
+ * ```
73
+ */
21
74
  export class GoodVibesSdkError extends Error {
75
+ kind;
22
76
  code;
23
77
  category;
24
78
  source;
@@ -40,6 +94,7 @@ export class GoodVibesSdkError extends Error {
40
94
  this.name = this.constructor.name;
41
95
  this.code = options.code;
42
96
  this.category = options.category ?? inferCategory(options.status);
97
+ this.kind = inferKind(this.category);
43
98
  this.source = options.source ?? 'unknown';
44
99
  this.recoverable = options.recoverable ?? (options.status !== undefined && RETRYABLE_STATUS_CODES.includes(options.status));
45
100
  this.status = options.status;
@@ -56,6 +111,27 @@ export class GoodVibesSdkError extends Error {
56
111
  this.retryAfterMs = options.retryAfterMs;
57
112
  }
58
113
  }
114
+ /**
115
+ * Thrown when the SDK is misconfigured (e.g. missing `baseUrl`, no fetch
116
+ * implementation available, or calling a mutation on a read-only auth resolver).
117
+ *
118
+ * Always non-recoverable (`recoverable: false`).
119
+ * Category: `'config'`. Kind: `'config'`.
120
+ *
121
+ * @deprecated Use `error.kind === 'config'` instead of `instanceof ConfigurationError`.
122
+ * This class is preserved for backward compatibility and still throws normally.
123
+ *
124
+ * @example
125
+ * import { ConfigurationError } from '@pellux/goodvibes-sdk';
126
+ *
127
+ * try {
128
+ * await sdk.auth.setToken('x');
129
+ * } catch (err) {
130
+ * if (err instanceof ConfigurationError) {
131
+ * // SDK was constructed with getAuthToken — token mutation not supported
132
+ * }
133
+ * }
134
+ */
59
135
  export class ConfigurationError extends GoodVibesSdkError {
60
136
  constructor(message, options = {}) {
61
137
  super(message, {
@@ -67,6 +143,28 @@ export class ConfigurationError extends GoodVibesSdkError {
67
143
  });
68
144
  }
69
145
  }
146
+ /**
147
+ * Thrown when a response from the daemon violates the expected contract
148
+ * (unexpected shape, missing required fields, etc.).
149
+ *
150
+ * Always non-recoverable (`recoverable: false`).
151
+ * Category: `'contract'`. Kind: `'contract'`.
152
+ *
153
+ * @deprecated Use `error.kind === 'contract'` instead of `instanceof ContractError`.
154
+ * This class is preserved for backward compatibility and still throws normally.
155
+ *
156
+ * @example
157
+ * import { ContractError } from '@pellux/goodvibes-sdk';
158
+ *
159
+ * try {
160
+ * const result = await sdk.operator.agents.get({ id: agentId });
161
+ * } catch (err) {
162
+ * if (err instanceof ContractError) {
163
+ * // Daemon returned an unexpected shape — SDK version mismatch?
164
+ * console.error('Contract violation:', err.message);
165
+ * }
166
+ * }
167
+ */
70
168
  export class ContractError extends GoodVibesSdkError {
71
169
  constructor(message, options = {}) {
72
170
  super(message, {
@@ -78,6 +176,36 @@ export class ContractError extends GoodVibesSdkError {
78
176
  });
79
177
  }
80
178
  }
179
+ /**
180
+ * Thrown when the daemon returns a non-2xx HTTP status code.
181
+ *
182
+ * The `category` field is inferred from the status code:
183
+ * - `401` → `'authentication'`  `402` → `'billing'`  `403` → `'authorization'`
184
+ * - `404` → `'not_found'`  `408` → `'timeout'`  `429` → `'rate_limit'`
185
+ * - `5xx` → `'service'`
186
+ *
187
+ * Use `recoverable` to decide whether to retry, and `retryAfterMs` for
188
+ * the backoff hint on rate-limit responses.
189
+ *
190
+ * @deprecated Use `error.kind` instead of `instanceof HttpStatusError`.
191
+ * For example: `error.kind === 'rate-limit'`, `error.kind === 'auth'`, `error.kind === 'server'`.
192
+ * This class is preserved for backward compatibility and still throws normally.
193
+ *
194
+ * @example
195
+ * import { HttpStatusError } from '@pellux/goodvibes-sdk';
196
+ *
197
+ * try {
198
+ * await sdk.operator.agents.list();
199
+ * } catch (err) {
200
+ * if (err instanceof HttpStatusError) {
201
+ * if (err.category === 'rate_limit') {
202
+ * await delay(err.retryAfterMs ?? 1000);
203
+ * } else if (!err.recoverable) {
204
+ * throw err; // Surface non-retryable errors immediately
205
+ * }
206
+ * }
207
+ * }
208
+ */
81
209
  export class HttpStatusError extends GoodVibesSdkError {
82
210
  constructor(message, options = {}) {
83
211
  super(message, {
@@ -0,0 +1,6 @@
1
+ export { OAuthClient } from './oauth-client.js';
2
+ export type { OAuthStartState, OAuthTokenPayload } from './oauth-client.js';
3
+ export { PermissionResolver } from './permission-resolver.js';
4
+ export { SessionManager } from './session-manager.js';
5
+ export { TokenStore } from './token-store.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { OAuthClient } from './oauth-client.js';
2
+ export { PermissionResolver } from './permission-resolver.js';
3
+ export { SessionManager } from './session-manager.js';
4
+ export { TokenStore } from './token-store.js';
@@ -0,0 +1,45 @@
1
+ /**
2
+ * OAuthClient — Focused responsibility: OAuth 2.0 / PKCE flows.
3
+ *
4
+ * Wraps the pure functions in `oauth-core.ts` behind a class boundary so
5
+ * callers can inject config once and call methods, rather than threading
6
+ * `OAuthProviderConfig` through every call.
7
+ */
8
+ import type { OAuthProviderConfig } from '../config/subscriptions.js';
9
+ import type { OAuthStartState, OAuthTokenPayload } from '../runtime/auth/oauth-core.js';
10
+ export type { OAuthStartState, OAuthTokenPayload };
11
+ export declare class OAuthClient {
12
+ #private;
13
+ constructor(config: OAuthProviderConfig);
14
+ /**
15
+ * Build the authorization URL and PKCE state needed to start an OAuth flow.
16
+ * Redirect the user's browser to `result.authorizationUrl`.
17
+ */
18
+ beginAuthorization(input?: {
19
+ readonly state?: string;
20
+ readonly verifier?: string;
21
+ readonly redirectUri?: string;
22
+ }): OAuthStartState;
23
+ /**
24
+ * Exchange an authorization code (returned via the redirect) for tokens.
25
+ * Use the `state` and `verifier` from the matching `beginAuthorization` call.
26
+ */
27
+ exchangeCode(input: {
28
+ readonly code: string;
29
+ readonly verifier: string;
30
+ readonly redirectUri: string;
31
+ readonly state?: string;
32
+ }): Promise<OAuthTokenPayload>;
33
+ /**
34
+ * Use a refresh token to obtain a new access token without user interaction.
35
+ */
36
+ refreshToken(refreshToken: string): Promise<OAuthTokenPayload>;
37
+ /**
38
+ * Decode a JWT access token's payload without verification.
39
+ * Returns null when the token is malformed.
40
+ */
41
+ decodeJwtPayload(token: string): Record<string, unknown> | null;
42
+ /** Expose the provider config this client was built from. */
43
+ get config(): OAuthProviderConfig;
44
+ }
45
+ //# sourceMappingURL=oauth-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-client.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/auth/oauth-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAOtE,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAClB,MAAM,+BAA+B,CAAC;AAEvC,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,CAAC;AAEnD,qBAAa,WAAW;;gBAGV,MAAM,EAAE,mBAAmB;IAIvC;;;OAGG;IACH,kBAAkB,CAAC,KAAK,CAAC,EAAE;QACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;KAC/B,GAAG,eAAe;IAInB;;;OAGG;IACG,YAAY,CAAC,KAAK,EAAE;QACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;KACzB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAI9B;;OAEG;IACG,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAIpE;;;OAGG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI/D,6DAA6D;IAC7D,IAAI,MAAM,IAAI,mBAAmB,CAEhC;CACF"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * OAuthClient — Focused responsibility: OAuth 2.0 / PKCE flows.
3
+ *
4
+ * Wraps the pure functions in `oauth-core.ts` behind a class boundary so
5
+ * callers can inject config once and call methods, rather than threading
6
+ * `OAuthProviderConfig` through every call.
7
+ */
8
+ import { buildOAuthAuthorizationStart, decodeJwtPayload, exchangeOAuthAuthorizationCode, refreshOAuthAccessToken, } from '../runtime/auth/oauth-core.js';
9
+ export class OAuthClient {
10
+ #config;
11
+ constructor(config) {
12
+ this.#config = config;
13
+ }
14
+ /**
15
+ * Build the authorization URL and PKCE state needed to start an OAuth flow.
16
+ * Redirect the user's browser to `result.authorizationUrl`.
17
+ */
18
+ beginAuthorization(input) {
19
+ return buildOAuthAuthorizationStart(this.#config, input);
20
+ }
21
+ /**
22
+ * Exchange an authorization code (returned via the redirect) for tokens.
23
+ * Use the `state` and `verifier` from the matching `beginAuthorization` call.
24
+ */
25
+ async exchangeCode(input) {
26
+ return exchangeOAuthAuthorizationCode(this.#config, input);
27
+ }
28
+ /**
29
+ * Use a refresh token to obtain a new access token without user interaction.
30
+ */
31
+ async refreshToken(refreshToken) {
32
+ return refreshOAuthAccessToken(this.#config, refreshToken);
33
+ }
34
+ /**
35
+ * Decode a JWT access token's payload without verification.
36
+ * Returns null when the token is malformed.
37
+ */
38
+ decodeJwtPayload(token) {
39
+ return decodeJwtPayload(token);
40
+ }
41
+ /** Expose the provider config this client was built from. */
42
+ get config() {
43
+ return this.#config;
44
+ }
45
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * PermissionResolver — Focused responsibility: role and scope checks.
3
+ *
4
+ * Inspects a `ControlPlaneAuthSnapshot` to answer permission questions.
5
+ * Callers that only need access control checks can use this directly
6
+ * rather than traversing the full auth snapshot themselves.
7
+ */
8
+ import type { ControlPlaneAuthSnapshot } from '../control-plane/auth-snapshot.js';
9
+ export declare class PermissionResolver {
10
+ #private;
11
+ constructor(snapshot: ControlPlaneAuthSnapshot);
12
+ /** Whether the current principal is authenticated. */
13
+ get authenticated(): boolean;
14
+ /** Whether the current principal has admin privileges. */
15
+ get isAdmin(): boolean;
16
+ /** The principal identifier (user/bot/service id), or null when anonymous. */
17
+ get principalId(): string | null;
18
+ /** The kind of the current principal. */
19
+ get principalKind(): ControlPlaneAuthSnapshot['principalKind'];
20
+ /** Return true when the principal holds the given role. */
21
+ hasRole(role: string): boolean;
22
+ /** Return true when the principal holds ALL of the given roles. */
23
+ hasAllRoles(roles: readonly string[]): boolean;
24
+ /** Return true when the principal holds ANY of the given roles. */
25
+ hasAnyRole(roles: readonly string[]): boolean;
26
+ /** Return true when the principal holds the given scope. */
27
+ hasScope(scope: string): boolean;
28
+ /** Return true when the principal holds ALL of the given scopes. */
29
+ hasAllScopes(scopes: readonly string[]): boolean;
30
+ /** Return true when the principal holds ANY of the given scopes. */
31
+ hasAnyScope(scopes: readonly string[]): boolean;
32
+ /** Expose the raw snapshot for direct inspection. */
33
+ get snapshot(): ControlPlaneAuthSnapshot;
34
+ }
35
+ //# sourceMappingURL=permission-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-resolver.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/auth/permission-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAElF,qBAAa,kBAAkB;;gBAGjB,QAAQ,EAAE,wBAAwB;IAI9C,sDAAsD;IACtD,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED,0DAA0D;IAC1D,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,8EAA8E;IAC9E,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED,yCAAyC;IACzC,IAAI,aAAa,IAAI,wBAAwB,CAAC,eAAe,CAAC,CAE7D;IAED,2DAA2D;IAC3D,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI9B,mEAAmE;IACnE,WAAW,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO;IAI9C,mEAAmE;IACnE,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO;IAI7C,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIhC,oEAAoE;IACpE,YAAY,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO;IAIhD,oEAAoE;IACpE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO;IAI/C,qDAAqD;IACrD,IAAI,QAAQ,IAAI,wBAAwB,CAEvC;CACF"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * PermissionResolver — Focused responsibility: role and scope checks.
3
+ *
4
+ * Inspects a `ControlPlaneAuthSnapshot` to answer permission questions.
5
+ * Callers that only need access control checks can use this directly
6
+ * rather than traversing the full auth snapshot themselves.
7
+ */
8
+ export class PermissionResolver {
9
+ #snapshot;
10
+ constructor(snapshot) {
11
+ this.#snapshot = snapshot;
12
+ }
13
+ /** Whether the current principal is authenticated. */
14
+ get authenticated() {
15
+ return this.#snapshot.authenticated;
16
+ }
17
+ /** Whether the current principal has admin privileges. */
18
+ get isAdmin() {
19
+ return this.#snapshot.admin;
20
+ }
21
+ /** The principal identifier (user/bot/service id), or null when anonymous. */
22
+ get principalId() {
23
+ return this.#snapshot.principalId;
24
+ }
25
+ /** The kind of the current principal. */
26
+ get principalKind() {
27
+ return this.#snapshot.principalKind;
28
+ }
29
+ /** Return true when the principal holds the given role. */
30
+ hasRole(role) {
31
+ return this.#snapshot.roles.includes(role);
32
+ }
33
+ /** Return true when the principal holds ALL of the given roles. */
34
+ hasAllRoles(roles) {
35
+ return roles.every((role) => this.#snapshot.roles.includes(role));
36
+ }
37
+ /** Return true when the principal holds ANY of the given roles. */
38
+ hasAnyRole(roles) {
39
+ return roles.some((role) => this.#snapshot.roles.includes(role));
40
+ }
41
+ /** Return true when the principal holds the given scope. */
42
+ hasScope(scope) {
43
+ return this.#snapshot.scopes.includes(scope);
44
+ }
45
+ /** Return true when the principal holds ALL of the given scopes. */
46
+ hasAllScopes(scopes) {
47
+ return scopes.every((scope) => this.#snapshot.scopes.includes(scope));
48
+ }
49
+ /** Return true when the principal holds ANY of the given scopes. */
50
+ hasAnyScope(scopes) {
51
+ return scopes.some((scope) => this.#snapshot.scopes.includes(scope));
52
+ }
53
+ /** Expose the raw snapshot for direct inspection. */
54
+ get snapshot() {
55
+ return this.#snapshot;
56
+ }
57
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * SessionManager — Focused responsibility: session lifecycle.
3
+ *
4
+ * Manages the login/logout lifecycle and ties token persistence to login
5
+ * results. Decoupled from transport and token storage implementations.
6
+ */
7
+ import type { OperatorSdk } from '../../operator/index.js';
8
+ import type { GoodVibesAuthLoginOptions, GoodVibesCurrentAuth, GoodVibesLoginInput, GoodVibesLoginOutput } from '../../../auth.js';
9
+ import { TokenStore } from './token-store.js';
10
+ export declare class SessionManager {
11
+ #private;
12
+ constructor(operator: OperatorSdk, tokenStore: TokenStore | null);
13
+ /**
14
+ * Return the current auth state from the daemon control plane.
15
+ * Does not require a writable token store.
16
+ */
17
+ current(): Promise<GoodVibesCurrentAuth>;
18
+ /**
19
+ * Perform a login and, when `persistToken` is not false, automatically
20
+ * persist the returned token into the configured token store.
21
+ */
22
+ login(input: GoodVibesLoginInput, options?: GoodVibesAuthLoginOptions): Promise<GoodVibesLoginOutput>;
23
+ /**
24
+ * Whether this session manager has a writable token store.
25
+ * Read-only instances (using a raw `getAuthToken` resolver) return false.
26
+ */
27
+ get writable(): boolean;
28
+ /** Access the underlying TokenStore (null when using a read-only resolver). */
29
+ get tokenStore(): TokenStore | null;
30
+ }
31
+ //# sourceMappingURL=session-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/auth/session-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EACV,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,qBAAa,cAAc;;gBAIb,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI;IAKhE;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAI9C;;;OAGG;IACG,KAAK,CACT,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,oBAAoB,CAAC;IAQhC;;;OAGG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,+EAA+E;IAC/E,IAAI,UAAU,IAAI,UAAU,GAAG,IAAI,CAElC;CACF"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * SessionManager — Focused responsibility: session lifecycle.
3
+ *
4
+ * Manages the login/logout lifecycle and ties token persistence to login
5
+ * results. Decoupled from transport and token storage implementations.
6
+ */
7
+ import { TokenStore } from './token-store.js';
8
+ export class SessionManager {
9
+ #operator;
10
+ #tokenStore;
11
+ constructor(operator, tokenStore) {
12
+ this.#operator = operator;
13
+ this.#tokenStore = tokenStore;
14
+ }
15
+ /**
16
+ * Return the current auth state from the daemon control plane.
17
+ * Does not require a writable token store.
18
+ */
19
+ async current() {
20
+ return this.#operator.control.auth.current();
21
+ }
22
+ /**
23
+ * Perform a login and, when `persistToken` is not false, automatically
24
+ * persist the returned token into the configured token store.
25
+ */
26
+ async login(input, options = {}) {
27
+ const result = await this.#operator.control.auth.login(input);
28
+ if ((options.persistToken ?? true) && this.#tokenStore) {
29
+ await this.#tokenStore.setToken(result.token);
30
+ }
31
+ return result;
32
+ }
33
+ /**
34
+ * Whether this session manager has a writable token store.
35
+ * Read-only instances (using a raw `getAuthToken` resolver) return false.
36
+ */
37
+ get writable() {
38
+ return this.#tokenStore !== null;
39
+ }
40
+ /** Access the underlying TokenStore (null when using a read-only resolver). */
41
+ get tokenStore() {
42
+ return this.#tokenStore;
43
+ }
44
+ }