@poolzin/pool-bot 2026.4.52 → 2026.4.54

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.
@@ -34,4 +34,8 @@ export declare function getApiKeyForModel(params: {
34
34
  agentDir?: string;
35
35
  }): Promise<ResolvedProviderAuth>;
36
36
  export declare function requireApiKey(auth: ResolvedProviderAuth, provider: string): string;
37
+ /**
38
+ * Resolve Nous Portal OAuth token.
39
+ */
40
+ export declare function resolveNousToken(store: AuthProfileStore): string | undefined;
37
41
  //# sourceMappingURL=model-auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-auth.d.ts","sourceRoot":"","sources":["../../src/agents/model-auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,GAAG,EAAgB,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD,OAAO,EACL,KAAK,gBAAgB,EAMtB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AA6BrF,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,aAAa,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS,CAIpB;AAwBD,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAOhG;AAqCD,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;CAC1D,CAAC;AAEF,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA8GhC;AAED,MAAM,MAAM,eAAe,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACjE,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,OAAO,GACP,OAAO,GACP,OAAO,GACP,SAAS,GACT,MAAM,GACN,SAAS,CAAC;AAEd,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAuFzE;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,CAAC,EAAE,MAAM,EACjB,GAAG,CAAC,EAAE,aAAa,EACnB,KAAK,CAAC,EAAE,gBAAgB,GACvB,aAAa,GAAG,SAAS,CAwC3B;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAC9C,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAShC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIlF"}
1
+ {"version":3,"file":"model-auth.d.ts","sourceRoot":"","sources":["../../src/agents/model-auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,GAAG,EAAgB,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD,OAAO,EACL,KAAK,gBAAgB,EAMtB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AA6BrF,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,aAAa,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS,CAIpB;AAwBD,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAOhG;AAqCD,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;CAC1D,CAAC;AAEF,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA8GhC;AAED,MAAM,MAAM,eAAe,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACjE,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,OAAO,GACP,OAAO,GACP,OAAO,GACP,SAAS,GACT,MAAM,GACN,SAAS,CAAC;AAEd,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAuFzE;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,CAAC,EAAE,MAAM,EACjB,GAAG,CAAC,EAAE,aAAa,EACnB,KAAK,CAAC,EAAE,gBAAgB,GACvB,aAAa,GAAG,SAAS,CAwC3B;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAC9C,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAShC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIlF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAiB5E"}
@@ -316,3 +316,23 @@ export function requireApiKey(auth, provider) {
316
316
  return key;
317
317
  throw new Error(`No API key resolved for provider "${provider}" (auth mode: ${auth.mode}).`);
318
318
  }
319
+ /**
320
+ * Resolve Nous Portal OAuth token.
321
+ */
322
+ export function resolveNousToken(store) {
323
+ const profileIds = listProfilesForProvider(store, "nous");
324
+ for (const id of profileIds) {
325
+ const cred = store.profiles[id];
326
+ if (!cred)
327
+ continue;
328
+ if (cred.type === "token" && cred.token?.trim()) {
329
+ // Check if token is expired
330
+ if (cred.expires && cred.expires < Date.now()) {
331
+ // Token expired
332
+ continue;
333
+ }
334
+ return cred.token.trim();
335
+ }
336
+ }
337
+ return undefined;
338
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../../src/agents/model-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAc,MAAM,qBAAqB,CAAC;AAQrE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;CACjC,CAAC;AAWF,KAAK,WAAW,GAAG,cAAc,yBAAyB,CAAC,CAAC;AAoC5D,wBAAgB,6BAA6B,SAI5C;AAGD,wBAAgB,8BAA8B,CAAC,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,QAEjF;AAUD,wBAAsB,gBAAgB,CAAC,MAAM,CAAC,EAAE;IAC9C,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA0F/B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,GAAG,OAAO,CAEjF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,iBAAiB,EAAE,EAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,iBAAiB,GAAG,SAAS,CAQ/B"}
1
+ {"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../../src/agents/model-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAc,MAAM,qBAAqB,CAAC;AASrE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;CACjC,CAAC;AAWF,KAAK,WAAW,GAAG,cAAc,yBAAyB,CAAC,CAAC;AAoC5D,wBAAgB,6BAA6B,SAI5C;AAGD,wBAAgB,8BAA8B,CAAC,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,QAEjF;AAUD,wBAAsB,gBAAgB,CAAC,MAAM,CAAC,EAAE;IAC9C,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAkG/B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,GAAG,OAAO,CAEjF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,iBAAiB,EAAE,EAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,iBAAiB,GAAG,SAAS,CAQ/B"}
@@ -3,6 +3,7 @@ import { createSubsystemLogger } from "../logging/subsystem.js";
3
3
  import { resolvePoolbotAgentDir } from "./agent-paths.js";
4
4
  import { ensurePoolbotModelsJson } from "./models-config.js";
5
5
  import { gemma4Models } from "./model-catalog-gemma4.js";
6
+ import { NOUS_MODELS } from "./nous-models.js";
6
7
  const log = createSubsystemLogger("model-catalog");
7
8
  let modelCatalogPromise = null;
8
9
  let hasLoggedModelCatalogError = false;
@@ -107,6 +108,13 @@ export async function loadModelCatalog(params) {
107
108
  if (models.length > 0) {
108
109
  // Add Gemma 4 models
109
110
  models.push(...gemma4Models);
111
+ // Add Nous Portal models
112
+ models.push(...NOUS_MODELS.map((m) => ({
113
+ id: m.id,
114
+ name: m.name,
115
+ provider: "nous",
116
+ contextWindow: m.contextWindow,
117
+ })));
110
118
  return sortModels(models);
111
119
  }
112
120
  // Add Gemma 4 models even if no other models
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Nous Portal Model Catalog
3
+ *
4
+ * Nous Research's portal provides access to various open-source models.
5
+ * Authentication: OAuth via portal.nousresearch.com
6
+ *
7
+ * @see https://portal.nousresearch.com
8
+ */
9
+ export declare const NOUS_BASE_URL = "https://api.nousresearch.com/v1";
10
+ export declare const NOUS_MODELS: readonly [{
11
+ readonly id: "nous-hermes-2";
12
+ readonly name: "Nous Hermes 2";
13
+ readonly contextWindow: 32768;
14
+ readonly description: "Nous Research's flagship model";
15
+ }, {
16
+ readonly id: "nous-hermes-2-mistral-7b-dpo";
17
+ readonly name: "Nous Hermes 2 Mistral 7B DPO";
18
+ readonly contextWindow: 32768;
19
+ readonly description: "Mistral-based model with DPO";
20
+ }, {
21
+ readonly id: "nous-capybara-34b";
22
+ readonly name: "Nous Capybara 34B";
23
+ readonly contextWindow: 32768;
24
+ readonly description: "Role-play focused model";
25
+ }, {
26
+ readonly id: "nous-hermes-llama2-13b";
27
+ readonly name: "Nous Hermes Llama2 13B";
28
+ readonly contextWindow: 4096;
29
+ readonly description: "Llama2-based model";
30
+ }];
31
+ export type NousModelId = typeof NOUS_MODELS[number]["id"];
32
+ export declare function getNousModel(modelId: string): typeof NOUS_MODELS[number] | undefined;
33
+ export declare function listNousModels(): typeof NOUS_MODELS;
34
+ //# sourceMappingURL=nous-models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nous-models.d.ts","sourceRoot":"","sources":["../../src/agents/nous-models.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAO,MAAM,aAAa,oCAAoC,CAAC;AAE/D,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;EAyBd,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAE3D,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,CAEpF;AAED,wBAAgB,cAAc,IAAI,OAAO,WAAW,CAEnD"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Nous Portal Model Catalog
3
+ *
4
+ * Nous Research's portal provides access to various open-source models.
5
+ * Authentication: OAuth via portal.nousresearch.com
6
+ *
7
+ * @see https://portal.nousresearch.com
8
+ */
9
+ export const NOUS_BASE_URL = "https://api.nousresearch.com/v1";
10
+ export const NOUS_MODELS = [
11
+ {
12
+ id: "nous-hermes-2",
13
+ name: "Nous Hermes 2",
14
+ contextWindow: 32768,
15
+ description: "Nous Research's flagship model",
16
+ },
17
+ {
18
+ id: "nous-hermes-2-mistral-7b-dpo",
19
+ name: "Nous Hermes 2 Mistral 7B DPO",
20
+ contextWindow: 32768,
21
+ description: "Mistral-based model with DPO",
22
+ },
23
+ {
24
+ id: "nous-capybara-34b",
25
+ name: "Nous Capybara 34B",
26
+ contextWindow: 32768,
27
+ description: "Role-play focused model",
28
+ },
29
+ {
30
+ id: "nous-hermes-llama2-13b",
31
+ name: "Nous Hermes Llama2 13B",
32
+ contextWindow: 4096,
33
+ description: "Llama2-based model",
34
+ },
35
+ ];
36
+ export function getNousModel(modelId) {
37
+ return NOUS_MODELS.find((m) => m.id === modelId);
38
+ }
39
+ export function listNousModels() {
40
+ return NOUS_MODELS;
41
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Nous Portal OAuth Authentication
3
+ *
4
+ * Handles OAuth 2.0 authentication flow for Nous Portal.
5
+ *
6
+ * Flow:
7
+ * 1. User authenticates at portal.nousresearch.com
8
+ * 2. Portal returns access token
9
+ * 3. Token is stored in auth profile store
10
+ * 4. Token is used for API requests
11
+ *
12
+ * @see https://portal.nousresearch.com
13
+ */
14
+ import { createServer } from "node:http";
15
+ export type NousOAuthResult = {
16
+ success: boolean;
17
+ accessToken?: string;
18
+ refreshToken?: string;
19
+ expiresAt?: number;
20
+ error?: string;
21
+ };
22
+ /**
23
+ * Generate PKCE code verifier and challenge for OAuth 2.0 PKCE flow.
24
+ */
25
+ export declare function generatePkce(): Promise<{
26
+ codeVerifier: string;
27
+ codeChallenge: string;
28
+ }>;
29
+ /**
30
+ * Open browser to Nous Portal OAuth page.
31
+ */
32
+ export declare function openNousOAuthUrl(params: {
33
+ codeChallenge: string;
34
+ redirectUri: string;
35
+ state: string;
36
+ }): Promise<string>;
37
+ /**
38
+ * Start local OAuth callback server and return the authorization code.
39
+ */
40
+ export declare function startOAuthCallbackServer(): Promise<{
41
+ code: string;
42
+ state: string;
43
+ server: ReturnType<typeof createServer>;
44
+ }>;
45
+ /**
46
+ * Exchange authorization code for access token.
47
+ */
48
+ export declare function exchangeCodeForToken(params: {
49
+ code: string;
50
+ codeVerifier: string;
51
+ redirectUri: string;
52
+ }): Promise<NousOAuthResult>;
53
+ /**
54
+ * Complete Nous Portal OAuth flow.
55
+ * Returns access token if successful.
56
+ */
57
+ export declare function completeNousOAuth(): Promise<NousOAuthResult>;
58
+ /**
59
+ * Refresh Nous Portal access token using refresh token.
60
+ */
61
+ export declare function refreshNousToken(params: {
62
+ refreshToken: string;
63
+ }): Promise<NousOAuthResult>;
64
+ //# sourceMappingURL=nous-oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nous-oauth.d.ts","sourceRoot":"","sources":["../../src/agents/nous-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,YAAY,EAA6C,MAAM,WAAW,CAAC;AAOpF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC,CASD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,MAAM,CAAC,CAalB;AAED;;GAEG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;CACzC,CAAC,CAmDD;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,eAAe,CAAC,CA4C3B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,eAAe,CAAC,CA0ClE;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,eAAe,CAAC,CA0C3B"}
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Nous Portal OAuth Authentication
3
+ *
4
+ * Handles OAuth 2.0 authentication flow for Nous Portal.
5
+ *
6
+ * Flow:
7
+ * 1. User authenticates at portal.nousresearch.com
8
+ * 2. Portal returns access token
9
+ * 3. Token is stored in auth profile store
10
+ * 4. Token is used for API requests
11
+ *
12
+ * @see https://portal.nousresearch.com
13
+ */
14
+ import { createServer } from "node:http";
15
+ import { randomBytes } from "node:crypto";
16
+ const NOUS_PORTAL_URL = "https://portal.nousresearch.com";
17
+ const NOUS_OAUTH_CLIENT_ID = "poolbot";
18
+ const NOUS_OAUTH_REDIRECT_PORT = 8889;
19
+ /**
20
+ * Generate PKCE code verifier and challenge for OAuth 2.0 PKCE flow.
21
+ */
22
+ export async function generatePkce() {
23
+ const codeVerifier = randomBytes(32).toString("base64url");
24
+ const encoder = new TextEncoder();
25
+ const data = encoder.encode(codeVerifier);
26
+ const digest = await crypto.subtle.digest("SHA-256", data);
27
+ const codeChallenge = Buffer.from(digest).toString("base64url");
28
+ return { codeVerifier, codeChallenge };
29
+ }
30
+ /**
31
+ * Open browser to Nous Portal OAuth page.
32
+ */
33
+ export async function openNousOAuthUrl(params) {
34
+ const { codeChallenge, redirectUri, state } = params;
35
+ const oauthUrl = new URL(`${NOUS_PORTAL_URL}/oauth/authorize`);
36
+ oauthUrl.searchParams.set("client_id", NOUS_OAUTH_CLIENT_ID);
37
+ oauthUrl.searchParams.set("response_type", "code");
38
+ oauthUrl.searchParams.set("redirect_uri", redirectUri);
39
+ oauthUrl.searchParams.set("scope", "api:read api:write");
40
+ oauthUrl.searchParams.set("state", state);
41
+ oauthUrl.searchParams.set("code_challenge", codeChallenge);
42
+ oauthUrl.searchParams.set("code_challenge_method", "S256");
43
+ return oauthUrl.toString();
44
+ }
45
+ /**
46
+ * Start local OAuth callback server and return the authorization code.
47
+ */
48
+ export async function startOAuthCallbackServer() {
49
+ return new Promise((resolve, reject) => {
50
+ const server = createServer(async (req, res) => {
51
+ if (req.url?.startsWith("/callback")) {
52
+ const url = new URL(req.url, `http://localhost:${NOUS_OAUTH_REDIRECT_PORT}`);
53
+ const code = url.searchParams.get("code");
54
+ const state = url.searchParams.get("state");
55
+ const error = url.searchParams.get("error");
56
+ if (error) {
57
+ res.writeHead(400);
58
+ res.end(`OAuth error: ${error}`);
59
+ reject(new Error(`OAuth error: ${error}`));
60
+ return;
61
+ }
62
+ if (!code || !state) {
63
+ res.writeHead(400);
64
+ res.end("Missing code or state parameter");
65
+ reject(new Error("Missing code or state parameter"));
66
+ return;
67
+ }
68
+ // Success page
69
+ res.writeHead(200, { "Content-Type": "text/html" });
70
+ res.end(`
71
+ <html>
72
+ <body style="font-family: sans-serif; text-align: center; padding: 50px;">
73
+ <h1>✅ Authentication Successful!</h1>
74
+ <p>You can close this window and return to the terminal.</p>
75
+ </body>
76
+ </html>
77
+ `);
78
+ resolve({ code, state, server });
79
+ }
80
+ else {
81
+ res.writeHead(404);
82
+ res.end("Not found");
83
+ }
84
+ });
85
+ server.listen(NOUS_OAUTH_REDIRECT_PORT, () => {
86
+ console.log(`OAuth callback server listening on http://localhost:${NOUS_OAUTH_REDIRECT_PORT}/callback`);
87
+ });
88
+ // Timeout after 5 minutes
89
+ setTimeout(() => {
90
+ server.close();
91
+ reject(new Error("OAuth timeout - user did not complete authentication"));
92
+ }, 5 * 60 * 1000);
93
+ });
94
+ }
95
+ /**
96
+ * Exchange authorization code for access token.
97
+ */
98
+ export async function exchangeCodeForToken(params) {
99
+ const { code, codeVerifier, redirectUri } = params;
100
+ try {
101
+ const response = await fetch(`${NOUS_PORTAL_URL}/oauth/token`, {
102
+ method: "POST",
103
+ headers: {
104
+ "Content-Type": "application/json",
105
+ },
106
+ body: JSON.stringify({
107
+ grant_type: "authorization_code",
108
+ client_id: NOUS_OAUTH_CLIENT_ID,
109
+ code,
110
+ redirect_uri: redirectUri,
111
+ code_verifier: codeVerifier,
112
+ }),
113
+ });
114
+ if (!response.ok) {
115
+ const error = await response.text();
116
+ return {
117
+ success: false,
118
+ error: `Token exchange failed: ${error}`,
119
+ };
120
+ }
121
+ const data = await response.json();
122
+ return {
123
+ success: true,
124
+ accessToken: data.access_token,
125
+ refreshToken: data.refresh_token,
126
+ expiresAt: Date.now() + data.expires_in * 1000,
127
+ };
128
+ }
129
+ catch (error) {
130
+ return {
131
+ success: false,
132
+ error: `Token exchange error: ${error instanceof Error ? error.message : String(error)}`,
133
+ };
134
+ }
135
+ }
136
+ /**
137
+ * Complete Nous Portal OAuth flow.
138
+ * Returns access token if successful.
139
+ */
140
+ export async function completeNousOAuth() {
141
+ console.log("Starting Nous Portal OAuth authentication...");
142
+ console.log("");
143
+ // Generate PKCE
144
+ const { codeVerifier, codeChallenge } = await generatePkce();
145
+ const redirectUri = `http://localhost:${NOUS_OAUTH_REDIRECT_PORT}/callback`;
146
+ const state = randomBytes(16).toString("base64url");
147
+ // Get OAuth URL
148
+ const oauthUrl = await openNousOAuthUrl({
149
+ codeChallenge,
150
+ redirectUri,
151
+ state,
152
+ });
153
+ console.log("Please open the following URL in your browser:");
154
+ console.log("");
155
+ console.log(oauthUrl);
156
+ console.log("");
157
+ console.log("After authentication, you will be redirected to the callback URL.");
158
+ console.log("");
159
+ // Start callback server
160
+ const { code } = await startOAuthCallbackServer();
161
+ console.log("Authorization code received, exchanging for token...");
162
+ // Exchange code for token
163
+ const result = await exchangeCodeForToken({
164
+ code,
165
+ codeVerifier,
166
+ redirectUri,
167
+ });
168
+ if (result.success) {
169
+ console.log("✅ Authentication successful!");
170
+ }
171
+ else {
172
+ console.log("❌ Authentication failed:", result.error);
173
+ }
174
+ return result;
175
+ }
176
+ /**
177
+ * Refresh Nous Portal access token using refresh token.
178
+ */
179
+ export async function refreshNousToken(params) {
180
+ const { refreshToken } = params;
181
+ try {
182
+ const response = await fetch(`${NOUS_PORTAL_URL}/oauth/token`, {
183
+ method: "POST",
184
+ headers: {
185
+ "Content-Type": "application/json",
186
+ },
187
+ body: JSON.stringify({
188
+ grant_type: "refresh_token",
189
+ client_id: NOUS_OAUTH_CLIENT_ID,
190
+ refresh_token: refreshToken,
191
+ }),
192
+ });
193
+ if (!response.ok) {
194
+ const error = await response.text();
195
+ return {
196
+ success: false,
197
+ error: `Token refresh failed: ${error}`,
198
+ };
199
+ }
200
+ const data = await response.json();
201
+ return {
202
+ success: true,
203
+ accessToken: data.access_token,
204
+ refreshToken: data.refresh_token,
205
+ expiresAt: Date.now() + data.expires_in * 1000,
206
+ };
207
+ }
208
+ catch (error) {
209
+ return {
210
+ success: false,
211
+ error: `Token refresh error: ${error instanceof Error ? error.message : String(error)}`,
212
+ };
213
+ }
214
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2026.4.52",
3
- "commit": "33af93cba3aa13f3fd09424f6dc03d6e0010d283",
4
- "builtAt": "2026-04-09T03:19:04.596Z"
2
+ "version": "2026.4.54",
3
+ "commit": "e39a9ab6a078425c4ecc8428db3923797dbda064",
4
+ "builtAt": "2026-04-09T18:45:09.725Z"
5
5
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Nous Portal OAuth CLI
3
+ *
4
+ * Commands for authenticating with Nous Portal.
5
+ */
6
+ import type { Command } from "commander";
7
+ export declare function registerNousAuthCli(program: Command): void;
8
+ //# sourceMappingURL=nous-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nous-auth.d.ts","sourceRoot":"","sources":["../../src/cli/nous-auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QAgJnD"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Nous Portal OAuth CLI
3
+ *
4
+ * Commands for authenticating with Nous Portal.
5
+ */
6
+ import { defaultRuntime } from "../runtime.js";
7
+ import { danger, info, success } from "../globals.js";
8
+ import { formatDocsLink } from "../terminal/links.js";
9
+ import { theme } from "../terminal/theme.js";
10
+ import { ensureAuthProfileStore } from "../agents/auth-profiles.js";
11
+ import { completeNousOAuth } from "../agents/nous-oauth.js";
12
+ import { saveAuthProfileStore } from "../agents/auth-profiles/store.js";
13
+ export function registerNousAuthCli(program) {
14
+ const nous = program
15
+ .command("nous")
16
+ .description("Nous Portal authentication")
17
+ .addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/providers/nous", "docs.molt.bot/providers/nous")}\n`);
18
+ nous
19
+ .command("login")
20
+ .description("Authenticate with Nous Portal via OAuth")
21
+ .action(async () => {
22
+ try {
23
+ defaultRuntime.log("");
24
+ defaultRuntime.log(theme.heading("Nous Portal Authentication"));
25
+ defaultRuntime.log(theme.muted("─────────────────────────────────────────"));
26
+ defaultRuntime.log("");
27
+ // Complete OAuth flow
28
+ const result = await completeNousOAuth();
29
+ if (!result.success) {
30
+ defaultRuntime.log(danger(`❌ Authentication failed: ${result.error}`));
31
+ defaultRuntime.exit(1);
32
+ return;
33
+ }
34
+ // Store token in auth profile store
35
+ const store = ensureAuthProfileStore();
36
+ const profileId = `nous:oauth:${Date.now()}`;
37
+ // Use TokenCredential type for OAuth access token
38
+ store.profiles[profileId] = {
39
+ type: "token",
40
+ provider: "nous",
41
+ token: result.accessToken,
42
+ expires: result.expiresAt,
43
+ };
44
+ // Save store
45
+ saveAuthProfileStore(store, undefined);
46
+ defaultRuntime.log("");
47
+ defaultRuntime.log(success("✅ Authentication successful!"));
48
+ defaultRuntime.log("");
49
+ defaultRuntime.log(info("Token stored in auth profile store."));
50
+ defaultRuntime.log(info("You can now use Nous Portal models."));
51
+ defaultRuntime.log("");
52
+ defaultRuntime.log(`Example: poolbot message send --model nous/nous-hermes-2 "Hello!"`);
53
+ defaultRuntime.log("");
54
+ defaultRuntime.exit(0);
55
+ }
56
+ catch (error) {
57
+ defaultRuntime.log(danger(`Authentication error: ${error instanceof Error ? error.message : String(error)}`));
58
+ defaultRuntime.exit(1);
59
+ }
60
+ });
61
+ nous
62
+ .command("status")
63
+ .description("Check Nous Portal authentication status")
64
+ .action(async () => {
65
+ try {
66
+ const store = ensureAuthProfileStore();
67
+ const profileIds = Object.entries(store.profiles)
68
+ .filter(([_, profile]) => profile.provider === "nous")
69
+ .map(([id]) => id);
70
+ if (profileIds.length === 0) {
71
+ defaultRuntime.log("");
72
+ defaultRuntime.log(info("No Nous Portal authentication found."));
73
+ defaultRuntime.log("");
74
+ defaultRuntime.log("Run 'poolbot nous login' to authenticate.");
75
+ defaultRuntime.log("");
76
+ defaultRuntime.exit(0);
77
+ return;
78
+ }
79
+ defaultRuntime.log("");
80
+ defaultRuntime.log(theme.heading("Nous Portal Authentication Status"));
81
+ defaultRuntime.log(theme.muted("─────────────────────────────────────────"));
82
+ defaultRuntime.log("");
83
+ for (const profileId of profileIds) {
84
+ const profile = store.profiles[profileId];
85
+ const isExpired = profile.type === "token" && profile.expires && profile.expires < Date.now();
86
+ const status = isExpired ? danger("❌ Expired") : success("✅ Valid");
87
+ defaultRuntime.log(`Profile: ${profileId}`);
88
+ defaultRuntime.log(` Status: ${status}`);
89
+ if (profile.type === "token" && profile.expires) {
90
+ const expires = new Date(profile.expires).toLocaleString();
91
+ defaultRuntime.log(` Expires: ${expires}`);
92
+ }
93
+ defaultRuntime.log("");
94
+ }
95
+ defaultRuntime.exit(0);
96
+ }
97
+ catch (error) {
98
+ defaultRuntime.log(danger(`Status check error: ${error instanceof Error ? error.message : String(error)}`));
99
+ defaultRuntime.exit(1);
100
+ }
101
+ });
102
+ nous
103
+ .command("logout")
104
+ .description("Remove Nous Portal authentication")
105
+ .option("--all", "Remove all Nous Portal tokens", false)
106
+ .action(async (opts) => {
107
+ try {
108
+ const store = ensureAuthProfileStore();
109
+ const profileIds = Object.entries(store.profiles)
110
+ .filter(([_, profile]) => profile.provider === "nous")
111
+ .map(([id]) => id);
112
+ if (profileIds.length === 0) {
113
+ defaultRuntime.log("");
114
+ defaultRuntime.log(info("No Nous Portal authentication found."));
115
+ defaultRuntime.log("");
116
+ defaultRuntime.exit(0);
117
+ return;
118
+ }
119
+ const toRemove = opts.all ? profileIds : [profileIds[0]];
120
+ for (const profileId of toRemove) {
121
+ delete store.profiles[profileId];
122
+ }
123
+ // Save store
124
+ saveAuthProfileStore(store, undefined);
125
+ defaultRuntime.log("");
126
+ defaultRuntime.log(success(`✅ Removed ${toRemove.length} Nous Portal token(s).`));
127
+ defaultRuntime.log("");
128
+ defaultRuntime.exit(0);
129
+ }
130
+ catch (error) {
131
+ defaultRuntime.log(danger(`Logout error: ${error instanceof Error ? error.message : String(error)}`));
132
+ defaultRuntime.exit(1);
133
+ }
134
+ });
135
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"register.subclis.d.ts","sourceRoot":"","sources":["../../../src/cli/program/register.subclis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,KAAK,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAElE,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,eAAe,CAAC;CAC3B,CAAC;AAyTF,wBAAgB,gBAAgB,IAAI,WAAW,EAAE,CAEhD;AAED,wBAAgB,gCAAgC,IAAI,MAAM,EAAE,CAE3D;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ3F;AAaD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,GAAE,MAAM,EAAiB,QAkBrF"}
1
+ {"version":3,"file":"register.subclis.d.ts","sourceRoot":"","sources":["../../../src/cli/program/register.subclis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,KAAK,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAElE,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,eAAe,CAAC;CAC3B,CAAC;AAkUF,wBAAgB,gBAAgB,IAAI,WAAW,EAAE,CAEhD;AAED,wBAAgB,gCAAgC,IAAI,MAAM,EAAE,CAE3D;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ3F;AAaD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,GAAE,MAAM,EAAiB,QAkBrF"}
@@ -308,6 +308,15 @@ const entries = [
308
308
  mod.registerTelemetryCli(program);
309
309
  },
310
310
  },
311
+ {
312
+ name: "nous",
313
+ description: "Nous Portal authentication and models",
314
+ hasSubcommands: true,
315
+ register: async (program) => {
316
+ const mod = await import("../nous-auth.js");
317
+ mod.registerNousAuthCli(program);
318
+ },
319
+ },
311
320
  ];
312
321
  export function getSubCliEntries() {
313
322
  return entries;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poolzin/pool-bot",
3
- "version": "2026.4.52",
3
+ "version": "2026.4.54",
4
4
  "description": "🎱 Pool Bot - AI assistant with PLCODE integrations",
5
5
  "keywords": [],
6
6
  "license": "MIT",