@robota-sdk/agent-command 3.0.0-beta.64 → 3.0.0-beta.66

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 (57) hide show
  1. package/README.md +74 -0
  2. package/dist/node/index.cjs +28 -28
  3. package/dist/node/index.d.ts +35 -5
  4. package/dist/node/index.d.ts.map +1 -1
  5. package/dist/node/index.js +28 -28
  6. package/dist/node/index.js.map +1 -1
  7. package/package.json +5 -3
  8. package/src/agent/agent-command-module.ts +2 -1
  9. package/src/agent/agent-command.ts +4 -2
  10. package/src/background/background-command-module.ts +7 -5
  11. package/src/background/background-command.ts +2 -1
  12. package/src/compact/compact-command-module.ts +2 -1
  13. package/src/compact/compact-command.ts +2 -1
  14. package/src/context/context-command-module.ts +2 -1
  15. package/src/context/context-command.ts +7 -6
  16. package/src/default/default-command-modules.ts +64 -0
  17. package/src/default/index.ts +2 -0
  18. package/src/exit/exit-command-module.ts +4 -2
  19. package/src/exit/exit-command.ts +2 -1
  20. package/src/help/help-command-module.ts +4 -2
  21. package/src/help/help-command.ts +2 -1
  22. package/src/index.ts +3 -0
  23. package/src/language/language-command-module.ts +8 -6
  24. package/src/language/language-command.ts +2 -1
  25. package/src/memory/memory-command-module.ts +8 -6
  26. package/src/memory/memory-command.ts +9 -8
  27. package/src/mode/mode-command-module.ts +8 -6
  28. package/src/mode/mode-command.ts +2 -1
  29. package/src/model/model-command-module.ts +8 -6
  30. package/src/model/model-command.ts +2 -1
  31. package/src/permissions/permissions-command-module.ts +8 -6
  32. package/src/permissions/permissions-command.ts +2 -1
  33. package/src/plugin/plugin-command-module.ts +8 -6
  34. package/src/plugin/plugin-command.ts +6 -5
  35. package/src/plugins/default-plugin-command-adapter.ts +164 -0
  36. package/src/plugins/default-plugin-command-source-loader.ts +31 -0
  37. package/src/provider/__tests__/provider-setup-flow.test.ts +204 -3
  38. package/src/provider/index.ts +2 -0
  39. package/src/provider/provider-command-execution.ts +11 -6
  40. package/src/provider/provider-command-module.ts +2 -1
  41. package/src/provider/provider-command-profile-lifecycle.ts +11 -6
  42. package/src/provider/provider-command-profile-operations.ts +11 -9
  43. package/src/provider/provider-command-profile.ts +12 -10
  44. package/src/provider/provider-command-setup.ts +10 -8
  45. package/src/provider/provider-setup-flow.ts +3 -2
  46. package/src/provider/provider-startup.ts +117 -0
  47. package/src/reset/reset-command-module.ts +2 -1
  48. package/src/rewind/rewind-command-module.ts +8 -6
  49. package/src/rewind/rewind-command.ts +7 -6
  50. package/src/session/session-command-module.ts +8 -6
  51. package/src/session/session-command.ts +2 -1
  52. package/src/skills/skills-command-module.ts +7 -7
  53. package/src/statusline/statusline-command-module.ts +8 -6
  54. package/src/statusline/statusline-command.ts +2 -1
  55. package/src/user-local/user-local-command-module.ts +6 -5
  56. package/src/user-local/user-local-command.ts +4 -2
  57. package/src/user-local/user-local-memory-command.ts +3 -1
@@ -0,0 +1,164 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+
5
+ import {
6
+ BundlePluginInstaller,
7
+ BundlePluginLoader,
8
+ MarketplaceClient,
9
+ PluginSettingsStore,
10
+ } from '@robota-sdk/agent-framework';
11
+
12
+ import type {
13
+ ICommandAvailablePlugin,
14
+ ICommandInstalledPlugin,
15
+ ICommandMarketplaceSource,
16
+ ICommandPluginAdapter,
17
+ IMarketplaceManifest,
18
+ TPluginInstallScope,
19
+ } from '@robota-sdk/agent-framework';
20
+
21
+ interface IPluginServices {
22
+ cwd: string;
23
+ marketplace: MarketplaceClient;
24
+ installer: BundlePluginInstaller;
25
+ loader: BundlePluginLoader;
26
+ settingsStore: PluginSettingsStore;
27
+ }
28
+
29
+ function createPluginServices(cwd: string): IPluginServices {
30
+ const home = homedir();
31
+ const pluginsDir = join(home, '.robota', 'plugins');
32
+ const userSettingsPath = join(home, '.robota', 'settings.json');
33
+
34
+ const exec = (command: string, options: { timeout: number; stdio?: string }): Buffer =>
35
+ execSync(command, {
36
+ timeout: options.timeout,
37
+ stdio: (options.stdio ?? 'pipe') as 'pipe' | 'inherit' | 'ignore',
38
+ });
39
+
40
+ const settingsStore = new PluginSettingsStore(userSettingsPath);
41
+ const marketplace = new MarketplaceClient({ pluginsDir, exec });
42
+ const installer = new BundlePluginInstaller({
43
+ pluginsDir,
44
+ settingsStore,
45
+ marketplaceClient: marketplace,
46
+ exec,
47
+ });
48
+ const loader = new BundlePluginLoader(pluginsDir);
49
+
50
+ return {
51
+ cwd,
52
+ marketplace,
53
+ installer,
54
+ loader,
55
+ settingsStore,
56
+ };
57
+ }
58
+
59
+ async function listInstalledPlugins(
60
+ services: IPluginServices,
61
+ ): Promise<readonly ICommandInstalledPlugin[]> {
62
+ const plugins = await services.loader.loadAll();
63
+ const enabledMap = services.settingsStore.getEnabledPlugins();
64
+ return plugins.map((plugin) => {
65
+ const parts = plugin.pluginDir.split('/');
66
+ const cacheIdx = parts.indexOf('cache');
67
+ const marketplaceName = cacheIdx >= 0 ? (parts[cacheIdx + 1] ?? '') : '';
68
+ const fullId = marketplaceName
69
+ ? `${plugin.manifest.name}@${marketplaceName}`
70
+ : plugin.manifest.name;
71
+ return {
72
+ name: fullId,
73
+ description: plugin.manifest.description,
74
+ enabled: enabledMap[fullId] !== false && enabledMap[plugin.manifest.name] !== false,
75
+ };
76
+ });
77
+ }
78
+
79
+ async function listAvailablePlugins(
80
+ services: IPluginServices,
81
+ marketplaceName: string,
82
+ ): Promise<readonly ICommandAvailablePlugin[]> {
83
+ let manifest: IMarketplaceManifest;
84
+ try {
85
+ manifest = services.marketplace.fetchManifest(marketplaceName);
86
+ } catch {
87
+ // allow-fallback: marketplace manifest fetch failure is non-fatal — return empty list
88
+ return [];
89
+ }
90
+ const installed = services.installer.getInstalledPlugins();
91
+ const installedNames = new Set(Object.values(installed).map((record) => record.pluginName));
92
+ return manifest.plugins.map((plugin) => ({
93
+ name: plugin.name,
94
+ description: plugin.description,
95
+ installed: installedNames.has(plugin.name),
96
+ }));
97
+ }
98
+
99
+ async function installPlugin(
100
+ services: IPluginServices,
101
+ pluginId: string,
102
+ scope?: TPluginInstallScope,
103
+ ): Promise<void> {
104
+ const [name, marketplaceName] = pluginId.split('@');
105
+ if (!name || !marketplaceName) {
106
+ throw new Error('Plugin ID must be in format: name@marketplace');
107
+ }
108
+ if (scope === 'project') {
109
+ const projectPluginsDir = join(services.cwd, '.robota', 'plugins');
110
+ const projectExec = (command: string, options: { timeout: number; stdio?: string }): Buffer =>
111
+ execSync(command, {
112
+ timeout: options.timeout,
113
+ stdio: (options.stdio ?? 'pipe') as 'pipe' | 'inherit' | 'ignore',
114
+ });
115
+ const projectInstaller = new BundlePluginInstaller({
116
+ pluginsDir: projectPluginsDir,
117
+ settingsStore: services.settingsStore,
118
+ marketplaceClient: services.marketplace,
119
+ exec: projectExec,
120
+ });
121
+ await projectInstaller.install(name, marketplaceName);
122
+ return;
123
+ }
124
+ await services.installer.install(name, marketplaceName);
125
+ }
126
+
127
+ async function removeMarketplace(services: IPluginServices, name: string): Promise<void> {
128
+ const installedFromMarketplace = services.installer.getPluginsByMarketplace(name);
129
+ for (const record of installedFromMarketplace) {
130
+ await services.installer.uninstall(`${record.pluginName}@${record.marketplace}`);
131
+ }
132
+ services.marketplace.removeMarketplace(name);
133
+ }
134
+
135
+ function listMarketplaces(services: IPluginServices): readonly ICommandMarketplaceSource[] {
136
+ return services.marketplace.listMarketplaces().map((marketplaceEntry) => ({
137
+ name: marketplaceEntry.name,
138
+ type: marketplaceEntry.source.type,
139
+ }));
140
+ }
141
+
142
+ export function createDefaultPluginCommandAdapter(cwd: string): ICommandPluginAdapter {
143
+ const services = createPluginServices(cwd);
144
+ return {
145
+ listInstalled: () => listInstalledPlugins(services),
146
+ listAvailablePlugins: (marketplaceName) => listAvailablePlugins(services, marketplaceName),
147
+ install: (pluginId, scope) => installPlugin(services, pluginId, scope),
148
+ uninstall: async (pluginId) => services.installer.uninstall(pluginId),
149
+ enable: async (pluginId) => services.installer.enable(pluginId),
150
+ disable: async (pluginId) => services.installer.disable(pluginId),
151
+ marketplaceAdd: async (source) => {
152
+ if (source.includes('/') && !source.includes(':')) {
153
+ return services.marketplace.addMarketplace({ type: 'github', repo: source });
154
+ }
155
+ return services.marketplace.addMarketplace({ type: 'git', url: source });
156
+ },
157
+ marketplaceRemove: (name) => removeMarketplace(services, name),
158
+ marketplaceUpdate: async (name) => services.marketplace.updateMarketplace(name),
159
+ marketplaceList: async () => listMarketplaces(services),
160
+ reloadPlugins: async () => ({
161
+ loadedPluginCount: (await services.loader.loadAll()).length,
162
+ }),
163
+ };
164
+ }
@@ -0,0 +1,31 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+
4
+ import { BundlePluginLoader, PluginCommandSource } from '@robota-sdk/agent-framework';
5
+
6
+ import type { CommandRegistry } from '@robota-sdk/agent-framework';
7
+
8
+ const PLUGIN_SOURCE_NAME = 'plugin';
9
+
10
+ function getHomeDir(): string {
11
+ return process.env.HOME ?? homedir();
12
+ }
13
+
14
+ export function reloadPluginCommandSource(registry: CommandRegistry): number {
15
+ const pluginsDir = join(getHomeDir(), '.robota', 'plugins');
16
+ const loader = new BundlePluginLoader(pluginsDir);
17
+ try {
18
+ // allow-fallback: plugin load failure is non-fatal — clear source and return empty
19
+ const plugins = loader.loadPluginsSync();
20
+ if (plugins.length === 0) {
21
+ registry.replaceSource(PLUGIN_SOURCE_NAME);
22
+ return 0;
23
+ }
24
+ registry.replaceSource(PLUGIN_SOURCE_NAME, new PluginCommandSource(plugins));
25
+ return plugins.length;
26
+ } catch {
27
+ // allow-fallback: plugin load failure is non-fatal — clear source and return empty
28
+ registry.replaceSource(PLUGIN_SOURCE_NAME);
29
+ return 0;
30
+ }
31
+ }
@@ -1,15 +1,26 @@
1
- import { describe, expect, it } from 'vitest';
1
+ import { describe, expect, it, vi } from 'vitest';
2
2
  import type { IProviderDefinition } from '@robota-sdk/agent-core';
3
3
  import {
4
4
  createProviderSetupFlow,
5
5
  formatProviderSetupHelpLinks,
6
6
  formatProviderSetupPromptLabel,
7
+ formatProviderSetupSelectionPrompt,
7
8
  getProviderSetupStep,
9
+ resolveProviderSetupSelection,
10
+ runProviderSetupPromptFlow,
11
+ submitProviderSetupValue,
8
12
  } from '../provider-setup-flow.js';
9
13
 
14
+ const openaiDefaults = {
15
+ apiKey: '$ENV:OPENAI_API_KEY',
16
+ };
17
+
10
18
  const providerDefinitions: readonly IProviderDefinition[] = [
11
19
  {
12
20
  type: 'openai',
21
+ displayName: 'OpenAI',
22
+ description: 'Use OpenAI Responses API',
23
+ defaults: openaiDefaults,
13
24
  setupHelpLinks: [
14
25
  {
15
26
  kind: 'api-key',
@@ -17,7 +28,33 @@ const providerDefinitions: readonly IProviderDefinition[] = [
17
28
  url: 'https://platform.openai.com/api-keys',
18
29
  },
19
30
  ],
20
- setupSteps: [{ key: 'apiKey', title: 'OpenAI API key', masked: true }],
31
+ setupSteps: [
32
+ {
33
+ key: 'model',
34
+ title: 'OpenAI model',
35
+ required: true,
36
+ },
37
+ {
38
+ key: 'apiKey',
39
+ title: 'OpenAI API key',
40
+ defaultValue: openaiDefaults.apiKey,
41
+ masked: true,
42
+ },
43
+ ],
44
+ requiresApiKey: true,
45
+ createProvider: () => {
46
+ throw new Error('not used');
47
+ },
48
+ },
49
+ {
50
+ type: 'anthropic',
51
+ displayName: 'Anthropic',
52
+ description: 'Use Claude models through Anthropic',
53
+ defaults: { model: 'claude-sonnet-4-6' },
54
+ setupSteps: [
55
+ { key: 'apiKey', title: 'Anthropic API key', required: true, masked: true },
56
+ { key: 'model', title: 'Anthropic model', defaultValue: 'claude-sonnet-4-6' },
57
+ ],
21
58
  requiresApiKey: true,
22
59
  createProvider: () => {
23
60
  throw new Error('not used');
@@ -36,8 +73,172 @@ describe('provider setup flow', () => {
36
73
  expect(formatProviderSetupPromptLabel(getProviderSetupStep(state), state.setupHelpLinks)).toBe(
37
74
  [
38
75
  ' Setup help: API key: OpenAI API keys - https://platform.openai.com/api-keys',
39
- ' OpenAI API key: ',
76
+ ' OpenAI model: ',
40
77
  ].join('\n'),
41
78
  );
42
79
  });
80
+
81
+ it('formats provider selection prompts from injected provider definitions', () => {
82
+ expect(formatProviderSetupSelectionPrompt(providerDefinitions)).toBe(
83
+ [
84
+ ' Select provider:',
85
+ ' 1. OpenAI (openai) - Use OpenAI Responses API',
86
+ ' 2. Anthropic (anthropic) - Use Claude models through Anthropic',
87
+ ' Provider [1-2] (default: 1): ',
88
+ ].join('\n'),
89
+ );
90
+ });
91
+
92
+ it('resolves provider selection by index or type without provider-specific branches', () => {
93
+ expect(resolveProviderSetupSelection('2', providerDefinitions)).toBe('anthropic');
94
+ expect(resolveProviderSetupSelection('openai', providerDefinitions)).toBe('openai');
95
+ expect(resolveProviderSetupSelection('', providerDefinitions)).toBe('openai');
96
+ expect(() => resolveProviderSetupSelection('gemma', providerDefinitions)).toThrow(
97
+ 'Unknown provider: gemma',
98
+ );
99
+ });
100
+
101
+ it('builds OpenAI setup input from model and default API key submissions', () => {
102
+ let state = createProviderSetupFlow('openai', providerDefinitions);
103
+ expect(state.setupHelpLinks).toEqual([
104
+ {
105
+ kind: 'api-key',
106
+ label: 'OpenAI API keys',
107
+ url: 'https://platform.openai.com/api-keys',
108
+ },
109
+ ]);
110
+ expect(getProviderSetupStep(state)).toMatchObject({
111
+ key: 'model',
112
+ required: true,
113
+ });
114
+
115
+ const emptyModelResult = submitProviderSetupValue(state, '');
116
+ expect(emptyModelResult.status).toBe('error');
117
+
118
+ const modelResult = submitProviderSetupValue(state, 'gpt-4o');
119
+ expect(modelResult.status).toBe('next');
120
+ if (modelResult.status !== 'next') throw new Error('expected next');
121
+ state = modelResult.state;
122
+
123
+ const apiKeyResult = submitProviderSetupValue(state, '');
124
+ expect(apiKeyResult).toEqual({
125
+ status: 'complete',
126
+ input: {
127
+ profile: 'openai',
128
+ type: 'openai',
129
+ model: 'gpt-4o',
130
+ apiKey: openaiDefaults.apiKey,
131
+ setCurrent: true,
132
+ },
133
+ });
134
+ });
135
+
136
+ it('keeps Anthropic API key validation outside the TUI component', () => {
137
+ const state = createProviderSetupFlow('anthropic', providerDefinitions);
138
+
139
+ expect(submitProviderSetupValue(state, '')).toEqual({
140
+ status: 'error',
141
+ state,
142
+ message: 'Required',
143
+ });
144
+ });
145
+
146
+ it('suggests unique profile names from type and disambiguates duplicates', () => {
147
+ let state = createProviderSetupFlow('anthropic', providerDefinitions, {
148
+ existingProfileNames: ['anthropic'],
149
+ });
150
+ const apiKeyResult = submitProviderSetupValue(state, 'sk-ant-test');
151
+ expect(apiKeyResult.status).toBe('next');
152
+ if (apiKeyResult.status !== 'next') throw new Error('expected next');
153
+ state = apiKeyResult.state;
154
+
155
+ const modelResult = submitProviderSetupValue(state, '');
156
+
157
+ expect(modelResult).toEqual({
158
+ status: 'complete',
159
+ input: {
160
+ profile: 'anthropic-2',
161
+ type: 'anthropic',
162
+ model: 'claude-sonnet-4-6',
163
+ apiKey: 'sk-ant-test',
164
+ setCurrent: true,
165
+ },
166
+ });
167
+ });
168
+
169
+ it('builds edit input for a fixed profile from current profile values', () => {
170
+ let state = createProviderSetupFlow('anthropic', providerDefinitions, {
171
+ profileName: 'work-claude',
172
+ setCurrent: false,
173
+ initialValues: {
174
+ apiKey: '$ENV:ANTHROPIC_API_KEY',
175
+ model: 'claude-sonnet-4-6',
176
+ },
177
+ });
178
+
179
+ expect(getProviderSetupStep(state)).toMatchObject({
180
+ key: 'apiKey',
181
+ defaultValue: '$ENV:ANTHROPIC_API_KEY',
182
+ required: false,
183
+ });
184
+
185
+ const apiKeyResult = submitProviderSetupValue(state, '');
186
+ expect(apiKeyResult.status).toBe('next');
187
+ if (apiKeyResult.status !== 'next') throw new Error('expected next');
188
+ state = apiKeyResult.state;
189
+
190
+ const modelResult = submitProviderSetupValue(state, 'claude-opus-4-5');
191
+
192
+ expect(modelResult).toEqual({
193
+ status: 'complete',
194
+ input: {
195
+ profile: 'work-claude',
196
+ type: 'anthropic',
197
+ model: 'claude-opus-4-5',
198
+ apiKey: '$ENV:ANTHROPIC_API_KEY',
199
+ setCurrent: false,
200
+ },
201
+ });
202
+ });
203
+
204
+ it('runs prompt input with masked API key steps', async () => {
205
+ const promptInput = vi.fn(async (label: string) => (label.includes('model') ? 'gpt-4o' : ''));
206
+
207
+ const input = await runProviderSetupPromptFlow('openai', promptInput, providerDefinitions);
208
+
209
+ expect(input.model).toBe('gpt-4o');
210
+ expect(input.profile).toBe('openai');
211
+ expect(input.apiKey).toBe(openaiDefaults.apiKey);
212
+ expect(promptInput).toHaveBeenNthCalledWith(
213
+ 1,
214
+ formatProviderSetupPromptLabel(
215
+ {
216
+ key: 'model',
217
+ title: 'OpenAI model',
218
+ required: true,
219
+ },
220
+ providerDefinitions[0]?.setupHelpLinks,
221
+ ),
222
+ false,
223
+ );
224
+ expect(promptInput).toHaveBeenNthCalledWith(
225
+ 2,
226
+ formatProviderSetupPromptLabel(
227
+ {
228
+ key: 'apiKey',
229
+ title: 'OpenAI API key',
230
+ defaultValue: openaiDefaults.apiKey,
231
+ masked: true,
232
+ },
233
+ providerDefinitions[0]?.setupHelpLinks,
234
+ ),
235
+ true,
236
+ );
237
+ });
238
+
239
+ it('formats provider setup help links for generic setup descriptions', () => {
240
+ expect(formatProviderSetupHelpLinks(providerDefinitions[0]?.setupHelpLinks)).toBe(
241
+ ' Setup help: API key: OpenAI API keys - https://platform.openai.com/api-keys',
242
+ );
243
+ });
43
244
  });
@@ -28,3 +28,5 @@ export type {
28
28
  IProviderCommandModuleOptions,
29
29
  IProviderCommandSettingsAdapter,
30
30
  } from '@robota-sdk/agent-framework';
31
+ export { ensureProviderConfig, runProviderStartupSetup } from './provider-startup.js';
32
+ export type { IProviderStartupContext, IEnsureProviderConfigOptions } from './provider-startup.js';
@@ -1,14 +1,17 @@
1
1
  import { findProviderDefinition, formatSupportedProviderTypes } from '@robota-sdk/agent-core';
2
+ import { testProviderProfileCommand } from '@robota-sdk/agent-framework';
3
+
4
+ import { buildProviderSwitch } from './provider-command-profile-operations.js';
5
+ import { createProviderProfileSelectionInteraction } from './provider-command-profile.js';
6
+ import { createSetupFlow, createProviderSetupInteraction } from './provider-command-setup.js';
7
+ import { formatProviderSetupChoiceLabel } from './provider-setup-flow.js';
8
+
2
9
  import type {
10
+ ICommandInteraction,
3
11
  ICommandResult,
4
12
  IProviderCommandModuleOptions,
5
13
  IProviderProfileSettings,
6
14
  } from '@robota-sdk/agent-framework';
7
- import { testProviderProfileCommand } from '@robota-sdk/agent-framework';
8
- import { formatProviderSetupChoiceLabel } from './provider-setup-flow.js';
9
- import { createSetupFlow, createProviderSetupInteraction } from './provider-command-setup.js';
10
- import { buildProviderSwitch } from './provider-command-profile-operations.js';
11
- import { createProviderProfileSelectionInteraction } from './provider-command-profile.js';
12
15
 
13
16
  export async function executeProviderCommand(
14
17
  args: string,
@@ -126,7 +129,9 @@ function buildProviderSetup(
126
129
  };
127
130
  }
128
131
 
129
- function createProviderSelectionInteraction(options: IProviderCommandModuleOptions) {
132
+ function createProviderSelectionInteraction(
133
+ options: IProviderCommandModuleOptions,
134
+ ): ICommandInteraction {
130
135
  return {
131
136
  prompt: {
132
137
  kind: 'choice' as const,
@@ -1,3 +1,5 @@
1
+ import { executeProviderCommand } from './provider-command-execution.js';
2
+
1
3
  import type {
2
4
  ICommand,
3
5
  ICommandModule as TCommandModule,
@@ -6,7 +8,6 @@ import type {
6
8
  IProviderCommandSettingsAdapter,
7
9
  ISystemCommand as TSystemCommand,
8
10
  } from '@robota-sdk/agent-framework';
9
- import { executeProviderCommand } from './provider-command-execution.js';
10
11
  export type { IProviderCommandModuleOptions, IProviderCommandSettingsAdapter };
11
12
 
12
13
  function buildProviderSubcommands(): ICommand[] {
@@ -1,15 +1,17 @@
1
- import type {
2
- ICommandInteraction,
3
- ICommandResult,
4
- IProviderCommandModuleOptions,
5
- } from '@robota-sdk/agent-framework';
6
1
  import {
7
2
  deleteProviderProfile,
8
3
  sanitizeProviderProfileName,
9
4
  upsertProviderProfile,
10
5
  } from '@robota-sdk/agent-framework';
6
+
11
7
  import { formatProviderChoiceLabel } from './provider-command-profile-operations.js';
12
8
 
9
+ import type {
10
+ ICommandInteraction,
11
+ ICommandResult,
12
+ IProviderCommandModuleOptions,
13
+ } from '@robota-sdk/agent-framework';
14
+
13
15
  const YES = 'yes';
14
16
  const MAX_DUPLICATE_PROFILE_SUFFIX = 1000;
15
17
  const PROVIDER_RESTART_EFFECT = {
@@ -191,7 +193,10 @@ function completeActiveProviderDelete(
191
193
  };
192
194
  }
193
195
 
194
- function suggestDuplicateProfileName(profileName: string, existingProfileNames: readonly string[]) {
196
+ function suggestDuplicateProfileName(
197
+ profileName: string,
198
+ existingProfileNames: readonly string[],
199
+ ): string {
195
200
  const base = sanitizeProviderProfileName(`${profileName}-copy`) ?? 'provider-copy';
196
201
  if (!existingProfileNames.includes(base)) {
197
202
  return base;
@@ -1,21 +1,23 @@
1
- import type {
2
- ICommandInteraction,
3
- ICommandResult,
4
- IProviderCommandModuleOptions,
5
- IProviderProfileSettings,
6
- IProviderSetupInput,
7
- } from '@robota-sdk/agent-framework';
8
1
  import {
9
2
  buildProviderSetupPatch,
10
3
  setCurrentProvider,
11
4
  upsertProviderProfile,
12
5
  } from '@robota-sdk/agent-framework';
13
- import { createProviderSetupFlow, submitProviderSetupValue } from './provider-setup-flow.js';
14
- import type { IProviderSetupFlowState } from './provider-setup-flow.js';
6
+
15
7
  import {
16
8
  createProviderSetupInteraction,
17
9
  toProviderSetupStepPrompt,
18
10
  } from './provider-command-setup.js';
11
+ import { createProviderSetupFlow, submitProviderSetupValue } from './provider-setup-flow.js';
12
+
13
+ import type { IProviderSetupFlowState } from './provider-setup-flow.js';
14
+ import type {
15
+ ICommandInteraction,
16
+ ICommandResult,
17
+ IProviderCommandModuleOptions,
18
+ IProviderProfileSettings,
19
+ IProviderSetupInput,
20
+ } from '@robota-sdk/agent-framework';
19
21
 
20
22
  const YES = 'yes';
21
23
  const PROVIDER_RESTART_EFFECT = {
@@ -1,19 +1,21 @@
1
- import type {
2
- ICommandInteraction,
3
- ICommandResult,
4
- IProviderCommandModuleOptions,
5
- IProviderProfileSettings,
6
- } from '@robota-sdk/agent-framework';
7
1
  import { testProviderProfileCommand } from '@robota-sdk/agent-framework';
2
+
3
+ import {
4
+ buildProviderDelete,
5
+ buildProviderDuplicate,
6
+ } from './provider-command-profile-lifecycle.js';
8
7
  import {
9
8
  buildProviderEdit,
10
9
  buildProviderSwitch,
11
10
  formatProviderChoiceLabel,
12
11
  } from './provider-command-profile-operations.js';
13
- import {
14
- buildProviderDelete,
15
- buildProviderDuplicate,
16
- } from './provider-command-profile-lifecycle.js';
12
+
13
+ import type {
14
+ ICommandInteraction,
15
+ ICommandResult,
16
+ IProviderCommandModuleOptions,
17
+ IProviderProfileSettings,
18
+ } from '@robota-sdk/agent-framework';
17
19
 
18
20
  const ACTION_SWITCH = 'switch';
19
21
  const ACTION_EDIT = 'edit';
@@ -1,12 +1,5 @@
1
- import type {
2
- ICommandInteraction,
3
- ICommandResult,
4
- IProviderCommandModuleOptions,
5
- IProviderSetupInput,
6
- TCommandInteractionPrompt,
7
- } from '@robota-sdk/agent-framework';
8
1
  import { buildProviderSetupPatch, mergeProviderPatch } from '@robota-sdk/agent-framework';
9
- import type { IProviderSetupFlowState } from './provider-setup-flow.js';
2
+
10
3
  import {
11
4
  createProviderSetupFlow,
12
5
  formatProviderSetupHelpLinks,
@@ -15,6 +8,15 @@ import {
15
8
  validateProviderSetupValue,
16
9
  } from './provider-setup-flow.js';
17
10
 
11
+ import type { IProviderSetupFlowState } from './provider-setup-flow.js';
12
+ import type {
13
+ ICommandInteraction,
14
+ ICommandResult,
15
+ IProviderCommandModuleOptions,
16
+ IProviderSetupInput,
17
+ TCommandInteractionPrompt,
18
+ } from '@robota-sdk/agent-framework';
19
+
18
20
  const PROVIDER_RESTART_EFFECT = {
19
21
  type: 'session-restart-requested',
20
22
  reason: 'other',
@@ -1,12 +1,13 @@
1
+ import { findProviderDefinition, formatSupportedProviderTypes } from '@robota-sdk/agent-core';
2
+ import { suggestProviderProfileName } from '@robota-sdk/agent-framework';
3
+
1
4
  import type {
2
5
  IProviderDefinition,
3
6
  IProviderSetupHelpLink,
4
7
  IProviderSetupStepDefinition,
5
8
  TProviderSetupField,
6
9
  } from '@robota-sdk/agent-core';
7
- import { findProviderDefinition, formatSupportedProviderTypes } from '@robota-sdk/agent-core';
8
10
  import type { IProviderSetupInput } from '@robota-sdk/agent-framework';
9
- import { suggestProviderProfileName } from '@robota-sdk/agent-framework';
10
11
 
11
12
  export type TProviderSetupType = string;
12
13
  export type TPromptInput = (label: string, masked?: boolean) => Promise<string>;