@equinor/fusion-framework-cli-plugin-ai-base 3.0.0 → 4.0.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.
@@ -12,6 +12,8 @@ export declare const chatModelOption: import("commander").Option;
12
12
  export declare const embedModelOption: import("commander").Option;
13
13
  /** `--index-name` | `FUSION_AI_INDEX_NAME` */
14
14
  export declare const indexNameOption: import("commander").Option;
15
+ /** `-d, --debug` | `RUNNER_DEBUG` — enable verbose logging */
16
+ export declare const debugOption: import("commander").Option;
15
17
  declare const _default: {
16
18
  envOption: import("commander").Option;
17
19
  tokenOption: import("commander").Option;
@@ -20,5 +22,6 @@ declare const _default: {
20
22
  chatModelOption: import("commander").Option;
21
23
  embedModelOption: import("commander").Option;
22
24
  indexNameOption: import("commander").Option;
25
+ debugOption: import("commander").Option;
23
26
  };
24
27
  export default _default;
@@ -19,5 +19,6 @@ export declare const AiOptionsSchema: z.ZodObject<{
19
19
  chatModel: z.ZodOptional<z.ZodString>;
20
20
  embedModel: z.ZodOptional<z.ZodString>;
21
21
  indexName: z.ZodOptional<z.ZodString>;
22
+ debug: z.ZodDefault<z.z.ZodCoercedBoolean<unknown>>;
22
23
  }, z.z.core.$strip>;
23
24
  export type AiOptionsType = z.infer<typeof AiOptionsSchema>;
@@ -19,4 +19,6 @@ export interface AiOptions {
19
19
  embedModel?: string;
20
20
  /** Azure AI Search index name. Required for vector search / indexing operations. */
21
21
  indexName?: string;
22
+ /** Enable debug mode for verbose logging. */
23
+ debug?: boolean;
22
24
  }
@@ -6,10 +6,10 @@ export type FrameworkInstance = FusionFramework<[AIModule]>;
6
6
  /**
7
7
  * Creates a Fusion Framework instance with the AI module enabled.
8
8
  *
9
- * Initialises the Fusion Framework with service discovery and MSAL auth,
10
- * resolves the `'ai'` service endpoint, and pre-caches a bearer token.
11
- * If MSAL has no cached credentials, the CLI's interactive `auth login`
12
- * flow is spawned automatically before retrying.
9
+ * Uses Azure Identity's `DefaultAzureCredential` by default, which resolves
10
+ * credentials from the environment (OIDC, managed identity, Azure CLI, etc.).
11
+ * When an explicit token is provided via `--token` or `FUSION_TOKEN`, that
12
+ * token is used directly instead.
13
13
  *
14
14
  * @param options - CLI options resolved by {@link withOptions}.
15
15
  * @returns A fully initialised framework instance with the AI module.
@@ -1 +1 @@
1
- export declare const version = "3.0.0";
1
+ export declare const version = "4.0.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-cli-plugin-ai-base",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "description": "Base AI plugin package for Fusion Framework CLI",
5
5
  "type": "module",
6
6
  "main": "dist/esm/index.js",
@@ -50,12 +50,12 @@
50
50
  "@equinor/fusion-framework-module-ai": "4.0.0"
51
51
  },
52
52
  "peerDependencies": {
53
- "@equinor/fusion-framework-cli": "^14.2.7"
53
+ "@equinor/fusion-framework-cli": "^15.0.0"
54
54
  },
55
55
  "devDependencies": {
56
56
  "typescript": "^5.9.3",
57
57
  "vitest": "^4.1.0",
58
- "@equinor/fusion-framework-cli": "^14.2.7"
58
+ "@equinor/fusion-framework-cli": "^15.0.0"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "tsc -b",
@@ -51,6 +51,11 @@ export const indexNameOption = createOption(
51
51
  'Azure AI Search index name',
52
52
  ).env('FUSION_AI_INDEX_NAME');
53
53
 
54
+ /** `-d, --debug` | `RUNNER_DEBUG` — enable verbose logging */
55
+ export const debugOption = createOption('-d, --debug', 'Enable debug mode for verbose logging')
56
+ .env('RUNNER_DEBUG')
57
+ .default(false);
58
+
54
59
  export default {
55
60
  envOption,
56
61
  tokenOption,
@@ -59,4 +64,5 @@ export default {
59
64
  chatModelOption,
60
65
  embedModelOption,
61
66
  indexNameOption,
67
+ debugOption,
62
68
  };
@@ -21,6 +21,7 @@ export const AiOptionsSchema = z
21
21
  chatModel: z.string().min(1).optional(),
22
22
  embedModel: z.string().min(1).optional(),
23
23
  indexName: z.string().min(1).optional(),
24
+ debug: z.coerce.boolean().default(false),
24
25
  })
25
26
  .describe('Base Fusion AI command options');
26
27
 
@@ -19,4 +19,6 @@ export interface AiOptions {
19
19
  embedModel?: string;
20
20
  /** Azure AI Search index name. Required for vector search / indexing operations. */
21
21
  indexName?: string;
22
+ /** Enable debug mode for verbose logging. */
23
+ debug?: boolean;
22
24
  }
@@ -2,6 +2,7 @@ import { type Command, InvalidOptionArgumentError } from 'commander';
2
2
  import {
3
3
  chatModelOption,
4
4
  clientIdOption,
5
+ debugOption,
5
6
  embedModelOption,
6
7
  envOption,
7
8
  indexNameOption,
@@ -38,6 +39,7 @@ export const withOptions = (
38
39
  command.addOption(tokenOption);
39
40
  command.addOption(tenantIdOption);
40
41
  command.addOption(clientIdOption);
42
+ command.addOption(debugOption);
41
43
 
42
44
  if (args?.includeChat) command.addOption(chatModelOption);
43
45
  if (args?.includeEmbedding) command.addOption(embedModelOption);
@@ -5,59 +5,42 @@ import { initializeFramework, FusionEnv } from '@equinor/fusion-framework-cli/bi
5
5
  import type { FusionFrameworkSettings, FusionFramework } from '@equinor/fusion-framework-cli/bin';
6
6
  import type { AiOptions } from './options/index.js';
7
7
 
8
- import { execFileSync } from 'node:child_process';
9
-
10
8
  /** Initialized framework instance with the AI module. */
11
9
  export type FrameworkInstance = FusionFramework<[AIModule]>;
12
10
 
13
- /**
14
- * Check whether an error (possibly wrapped in a cause chain) is an
15
- * authentication-related failure that may be recoverable via interactive login.
16
- *
17
- * @internal
18
- */
19
- const isAuthError = (error: unknown): boolean => {
20
- let current: unknown = error;
21
- while (current) {
22
- if (current instanceof Error) {
23
- if (
24
- current.name === 'NoAccountsError' ||
25
- current.name === 'SilentTokenAcquisitionError' ||
26
- current.message.includes('No accounts found')
27
- ) {
28
- return true;
29
- }
30
- }
31
- current = (current as { cause?: unknown }).cause;
32
- }
33
- return false;
34
- };
35
-
36
11
  /**
37
12
  * Creates a Fusion Framework instance with the AI module enabled.
38
13
  *
39
- * Initialises the Fusion Framework with service discovery and MSAL auth,
40
- * resolves the `'ai'` service endpoint, and pre-caches a bearer token.
41
- * If MSAL has no cached credentials, the CLI's interactive `auth login`
42
- * flow is spawned automatically before retrying.
14
+ * Uses Azure Identity's `DefaultAzureCredential` by default, which resolves
15
+ * credentials from the environment (OIDC, managed identity, Azure CLI, etc.).
16
+ * When an explicit token is provided via `--token` or `FUSION_TOKEN`, that
17
+ * token is used directly instead.
43
18
  *
44
19
  * @param options - CLI options resolved by {@link withOptions}.
45
20
  * @returns A fully initialised framework instance with the AI module.
46
21
  * @throws {Error} When authentication fails after the interactive retry.
47
22
  */
48
23
  export const setupFramework = async (options: AiOptions): Promise<FusionFramework<[AIModule]>> => {
49
- // Service-discovery mode: resolve URL + scopes from Fusion service registry
24
+ const debug = options.debug ?? false;
25
+
26
+ // Auth strategy:
27
+ // 1. Explicit token (--token / FUSION_TOKEN) → direct token passthrough
28
+ // 2. Everything else → Azure Identity (DefaultAzureCredential)
50
29
  const auth: FusionFrameworkSettings['auth'] = options.token
51
30
  ? { token: options.token }
52
- : {
53
- tenantId: options.tenantId ?? '3aa4a235-b6e2-48d5-9195-7fcf05b459b0',
54
- clientId: options.clientId ?? 'a318b8e1-0295-4e17-98d5-35f67dfeba14',
55
- };
31
+ : { defaultCredential: true };
56
32
 
57
33
  const env = (options.env as FusionEnv) ?? FusionEnv.ContinuesIntegration;
58
34
 
35
+ if (debug) {
36
+ console.debug('[debug] Environment:', env);
37
+ console.debug('[debug] Auth mode:', options.token ? 'static-token' : 'azure-identity');
38
+ }
39
+
59
40
  /** Initialise the framework, resolve the AI service, and pre-cache tokens. */
60
41
  const initAndSetup = async (): Promise<FusionFramework<[AIModule]>> => {
42
+ if (debug) console.debug('[debug] Initializing framework with AI module…');
43
+
61
44
  const framework = await initializeFramework<[AIModule]>({ env, auth }, (configurator) => {
62
45
  enableAI(configurator);
63
46
  });
@@ -67,36 +50,22 @@ export const setupFramework = async (options: AiOptions): Promise<FusionFramewor
67
50
  const service = await framework.serviceDiscovery.resolveService('ai');
68
51
  const scopes = service.scopes ?? service.defaultScopes ?? [];
69
52
 
53
+ if (debug) {
54
+ console.debug('[debug] AI service URL:', service.uri);
55
+ console.debug('[debug] AI service scopes:', scopes);
56
+ }
57
+
70
58
  // Pre-cache a token for the AI service scopes so strategy callbacks
71
59
  // don't attempt (and fail) a silent acquisition later.
72
60
  const token = await framework.auth.acquireAccessToken({ request: { scopes } });
73
61
  if (!token) throw new Error('Failed to acquire access token for the AI service.');
74
62
 
63
+ if (debug) console.debug('[debug] Token acquired successfully');
64
+
75
65
  return framework;
76
66
  };
77
67
 
78
- try {
79
- return await initAndSetup();
80
- } catch (error: unknown) {
81
- // If the failure is auth-related and we're not using a static token,
82
- // spawn the CLI's own `auth login` (starts local server + browser)
83
- // and retry the full init sequence.
84
- if (!isAuthError(error) || options.token) throw error;
85
-
86
- const cliEntry = process.argv[1];
87
- if (!cliEntry) {
88
- throw new Error(
89
- 'Failed to acquire access token and could not determine CLI path for interactive login.',
90
- );
91
- }
92
-
93
- console.log('No cached credentials — launching interactive login…');
94
- execFileSync(process.execPath, [cliEntry, 'auth', 'login'], {
95
- stdio: 'inherit',
96
- });
97
-
98
- return await initAndSetup();
99
- }
68
+ return await initAndSetup();
100
69
  };
101
70
 
102
71
  export default setupFramework;
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '3.0.0';
2
+ export const version = '4.0.0';