@agents-at-scale/ark 0.1.64 → 0.1.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.
@@ -1,7 +1,9 @@
1
1
  import { Command } from 'commander';
2
2
  import { execa } from 'execa';
3
+ import chalk from 'chalk';
3
4
  import output from '../../lib/output.js';
4
- import { executeQuery } from '../../lib/executeQuery.js';
5
+ import { executeQuery, parseParameters } from '../../lib/executeQuery.js';
6
+ import { ExitCodes } from '../../lib/errors.js';
5
7
  async function listAgents(options) {
6
8
  try {
7
9
  // Use kubectl to get agents
@@ -55,12 +57,22 @@ export function createAgentsCommand(config) {
55
57
  .argument('<name>', 'Agent name')
56
58
  .argument('<message>', 'Message to send')
57
59
  .option('--timeout <timeout>', 'Query timeout (e.g., 30s, 5m, 1h)')
60
+ .option('-p, --parameter <name=value>', 'Template parameter in name=value format (can be used multiple times)', (val, acc) => [...acc, val], [])
58
61
  .action(async (name, message, options) => {
62
+ let parameters;
63
+ try {
64
+ parameters = parseParameters(options.parameter || []);
65
+ }
66
+ catch (error) {
67
+ console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));
68
+ process.exit(ExitCodes.CliError);
69
+ }
59
70
  await executeQuery({
60
71
  targetType: 'agent',
61
72
  targetName: name,
62
73
  message,
63
74
  timeout: options.timeout || config.queryTimeout,
75
+ parameters,
64
76
  });
65
77
  });
66
78
  return agentsCommand;
@@ -50,7 +50,7 @@ function backendInstallArgs(service, backend, values) {
50
50
  function isValidVersion(version) {
51
51
  return /^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version);
52
52
  }
53
- function isVersionNotFoundError(error, options) {
53
+ function notFoundVersion(error, options) {
54
54
  let errorMsg = '';
55
55
  if (error && typeof error === 'object') {
56
56
  const err = error;
@@ -61,24 +61,30 @@ function isVersionNotFoundError(error, options) {
61
61
  errorMsg = String(error);
62
62
  }
63
63
  if (options.arkVersion && errorMsg.includes(`:${options.arkVersion}: not found`)) {
64
- return true;
64
+ return options.arkVersion;
65
65
  }
66
66
  if (options.marketplaceVersion && errorMsg.includes(`:${options.marketplaceVersion}: not found`)) {
67
- return true;
67
+ return options.marketplaceVersion;
68
68
  }
69
- return false;
69
+ return null;
70
70
  }
71
71
  function handleInstallError(error, service, options) {
72
- if (isVersionNotFoundError(error, options)) {
73
- const version = options.arkVersion || options.marketplaceVersion;
74
- output.warning(`${service.name} version ${version} not found, skipping...`);
75
- return true; // should continue to next service
72
+ const version = notFoundVersion(error, options);
73
+ if (version !== null) {
74
+ return version; // should continue to next service
76
75
  }
77
76
  // Other errors still fail
78
77
  output.error(`failed to install ${service.name}`);
79
78
  console.error(error);
80
79
  process.exit(1);
81
80
  }
81
+ function exitIfServicesSkipped(skipped) {
82
+ if (skipped.length === 0)
83
+ return;
84
+ const detail = skipped.map((s) => `${s.name}@${s.version}`).join(', ');
85
+ output.error(`installation incomplete: ${skipped.length} service(s) skipped because the requested version was not found: ${detail}`);
86
+ process.exit(1);
87
+ }
82
88
  async function uninstallPrerequisites(service, verbose = false) {
83
89
  if (!service.prerequisiteUninstalls?.length)
84
90
  return;
@@ -185,6 +191,7 @@ export async function installArk(config, serviceNames = [], options = {}) {
185
191
  process.exit(1);
186
192
  }
187
193
  const backend = requestedBackend;
194
+ const skippedServices = [];
188
195
  let postgresValues;
189
196
  if (backend === 'postgresql') {
190
197
  try {
@@ -233,7 +240,9 @@ export async function installArk(config, serviceNames = [], options = {}) {
233
240
  output.success(`${service.name} installed successfully`);
234
241
  }
235
242
  catch (error) {
236
- if (handleInstallError(error, service, options)) {
243
+ const skippedVersion = handleInstallError(error, service, options);
244
+ if (skippedVersion) {
245
+ skippedServices.push({ name: service.name, version: skippedVersion });
237
246
  continue;
238
247
  }
239
248
  }
@@ -258,7 +267,7 @@ export async function installArk(config, serviceNames = [], options = {}) {
258
267
  if (service.helmReleaseName === 'ark-apiserver' && backend === 'postgresql') {
259
268
  const spinner = ora('Waiting for ark-apiserver to be ready...').start();
260
269
  try {
261
- const results = await runReadinessChecks(120); // 2 minute timeout
270
+ const results = await runReadinessChecks(120, backend); // 2 minute timeout
262
271
  const failed = results.find((r) => !r.passed);
263
272
  if (failed) {
264
273
  spinner.fail(`ark-apiserver readiness check failed: ${failed.message || 'unknown error'}`);
@@ -274,11 +283,14 @@ export async function installArk(config, serviceNames = [], options = {}) {
274
283
  }
275
284
  }
276
285
  catch (error) {
277
- if (handleInstallError(error, service, options)) {
286
+ const skippedVersion = handleInstallError(error, service, options);
287
+ if (skippedVersion) {
288
+ skippedServices.push({ name: service.name, version: skippedVersion });
278
289
  continue;
279
290
  }
280
291
  }
281
292
  }
293
+ exitIfServicesSkipped(skippedServices);
282
294
  return;
283
295
  }
284
296
  // If not using -y flag, show checklist interface
@@ -417,7 +429,7 @@ export async function installArk(config, serviceNames = [], options = {}) {
417
429
  if (service.helmReleaseName === 'ark-apiserver' && backend === 'postgresql') {
418
430
  const spinner = ora('Waiting for ark-apiserver to be ready...').start();
419
431
  try {
420
- const results = await runReadinessChecks(120); // 2 minute timeout
432
+ const results = await runReadinessChecks(120, backend); // 2 minute timeout
421
433
  const failed = results.find((r) => !r.passed);
422
434
  if (failed) {
423
435
  spinner.fail(`ark-apiserver readiness check failed: ${failed.message || 'unknown error'}`);
@@ -434,7 +446,9 @@ export async function installArk(config, serviceNames = [], options = {}) {
434
446
  console.log(); // Add blank line after command output
435
447
  }
436
448
  catch (error) {
437
- if (handleInstallError(error, service, options)) {
449
+ const skippedVersion = handleInstallError(error, service, options);
450
+ if (skippedVersion) {
451
+ skippedServices.push({ name: service.name, version: skippedVersion });
438
452
  console.log(); // Add blank line after warning
439
453
  continue;
440
454
  }
@@ -476,7 +490,9 @@ export async function installArk(config, serviceNames = [], options = {}) {
476
490
  console.log(); // Add blank line after command output
477
491
  }
478
492
  catch (error) {
479
- if (handleInstallError(error, service, options)) {
493
+ const skippedVersion = handleInstallError(error, service, options);
494
+ if (skippedVersion) {
495
+ skippedServices.push({ name: service.name, version: skippedVersion });
480
496
  console.log(); // Add blank line after warning
481
497
  continue;
482
498
  }
@@ -496,7 +512,8 @@ export async function installArk(config, serviceNames = [], options = {}) {
496
512
  s.category === 'core' &&
497
513
  s.k8sDeploymentName &&
498
514
  s.namespace &&
499
- (!s.requiresBackend || s.requiresBackend === backend));
515
+ (!s.requiresBackend || s.requiresBackend === backend) &&
516
+ !skippedServices.some((sk) => sk.name === s.name));
500
517
  const spinner = ora(`Waiting for Ark to be ready (timeout: ${timeoutSeconds}s)...`).start();
501
518
  const statusMap = new Map();
502
519
  servicesToWait.forEach((s) => statusMap.set(s.name, false));
@@ -526,6 +543,7 @@ export async function installArk(config, serviceNames = [], options = {}) {
526
543
  process.exit(1);
527
544
  }
528
545
  }
546
+ exitIfServicesSkipped(skippedServices);
529
547
  }
530
548
  export function createInstallCommand(config) {
531
549
  const command = new Command('install');
@@ -23,6 +23,29 @@ export const defaultDeps = {
23
23
  };
24
24
  const POLL_INTERVAL_MS = 2000;
25
25
  const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
26
+ const LOOPBACK_LITERALS = new Set(['127.0.0.1', '::1', 'localhost']);
27
+ function warnIfCallbackUnreachable(authorizationUrl, proxyBaseUrl) {
28
+ let redirect;
29
+ try {
30
+ const raw = new URL(authorizationUrl).searchParams.get('redirect_uri');
31
+ if (!raw)
32
+ return;
33
+ redirect = new URL(raw);
34
+ }
35
+ catch {
36
+ return;
37
+ }
38
+ const host = redirect.hostname.replace(/^\[|\]$/g, '');
39
+ if (!LOOPBACK_LITERALS.has(host))
40
+ return;
41
+ const proxyPort = new URL(proxyBaseUrl).port;
42
+ if (redirect.port && proxyPort && redirect.port !== proxyPort) {
43
+ output.warning(`callback is configured for ${redirect.host} but the CLI port-forward is on ` +
44
+ `port ${proxyPort}; the browser redirect will not reach ark-api. Set ` +
45
+ `ARK_API_PUBLIC_CALLBACK_URL to use port ${proxyPort}, or port-forward ` +
46
+ `ark-api on port ${redirect.port}.`);
47
+ }
48
+ }
26
49
  export async function runLogin(serverName, options, deps = defaultDeps) {
27
50
  let timeoutMs = DEFAULT_TIMEOUT_MS;
28
51
  if (options.timeout !== undefined) {
@@ -57,6 +80,7 @@ export async function runLogin(serverName, options, deps = defaultDeps) {
57
80
  }
58
81
  return 1;
59
82
  }
83
+ warnIfCallbackUnreachable(startResponse.authorization_url, proxy.baseUrl);
60
84
  console.log(`Authorization URL: ${startResponse.authorization_url}`);
61
85
  if (options.open !== false) {
62
86
  try {
@@ -109,34 +109,46 @@ export class KubernetesModelManifestBuilder {
109
109
  region: {
110
110
  value: config.region,
111
111
  },
112
- accessKeyId: {
113
- valueFrom: {
114
- secretKeyRef: {
115
- name: config.secretName,
116
- key: 'access-key-id',
117
- },
112
+ },
113
+ };
114
+ const bedrock = bedrockConfig.bedrock;
115
+ if (config.authMethod === 'api-key') {
116
+ bedrock.apiKey = {
117
+ valueFrom: {
118
+ secretKeyRef: {
119
+ name: config.secretName,
120
+ key: 'bedrock-api-key',
118
121
  },
119
122
  },
120
- secretAccessKey: {
121
- valueFrom: {
122
- secretKeyRef: {
123
- name: config.secretName,
124
- key: 'secret-access-key',
125
- },
123
+ };
124
+ }
125
+ else {
126
+ bedrock.accessKeyId = {
127
+ valueFrom: {
128
+ secretKeyRef: {
129
+ name: config.secretName,
130
+ key: 'access-key-id',
126
131
  },
127
132
  },
128
- },
129
- };
130
- const bedrock = bedrockConfig.bedrock;
131
- if (config.sessionToken) {
132
- bedrock.sessionToken = {
133
+ };
134
+ bedrock.secretAccessKey = {
133
135
  valueFrom: {
134
136
  secretKeyRef: {
135
137
  name: config.secretName,
136
- key: 'session-token',
138
+ key: 'secret-access-key',
137
139
  },
138
140
  },
139
141
  };
142
+ if (config.sessionToken) {
143
+ bedrock.sessionToken = {
144
+ valueFrom: {
145
+ secretKeyRef: {
146
+ name: config.secretName,
147
+ key: 'session-token',
148
+ },
149
+ },
150
+ };
151
+ }
140
152
  }
141
153
  if (config.modelArn) {
142
154
  bedrock.modelArn = {
@@ -23,10 +23,15 @@ export class KubernetesSecretManager {
23
23
  async createNewSecret(config) {
24
24
  const secretArgs = ['create', 'secret', 'generic', config.secretName];
25
25
  if (config.type === 'bedrock') {
26
- secretArgs.push(`--from-literal=access-key-id=${config.accessKeyId}`);
27
- secretArgs.push(`--from-literal=secret-access-key=${config.secretAccessKey}`);
28
- if (config.sessionToken) {
29
- secretArgs.push(`--from-literal=session-token=${config.sessionToken}`);
26
+ if (config.authMethod === 'api-key') {
27
+ secretArgs.push(`--from-literal=bedrock-api-key=${config.apiKey}`);
28
+ }
29
+ else {
30
+ secretArgs.push(`--from-literal=access-key-id=${config.accessKeyId}`);
31
+ secretArgs.push(`--from-literal=secret-access-key=${config.secretAccessKey}`);
32
+ if (config.sessionToken) {
33
+ secretArgs.push(`--from-literal=session-token=${config.sessionToken}`);
34
+ }
30
35
  }
31
36
  }
32
37
  else {
@@ -39,10 +44,15 @@ export class KubernetesSecretManager {
39
44
  async updateSecret(config) {
40
45
  const secretArgs = ['create', 'secret', 'generic', config.secretName];
41
46
  if (config.type === 'bedrock') {
42
- secretArgs.push(`--from-literal=access-key-id=${config.accessKeyId}`);
43
- secretArgs.push(`--from-literal=secret-access-key=${config.secretAccessKey}`);
44
- if (config.sessionToken) {
45
- secretArgs.push(`--from-literal=session-token=${config.sessionToken}`);
47
+ if (config.authMethod === 'api-key') {
48
+ secretArgs.push(`--from-literal=bedrock-api-key=${config.apiKey}`);
49
+ }
50
+ else {
51
+ secretArgs.push(`--from-literal=access-key-id=${config.accessKeyId}`);
52
+ secretArgs.push(`--from-literal=secret-access-key=${config.secretAccessKey}`);
53
+ if (config.sessionToken) {
54
+ secretArgs.push(`--from-literal=session-token=${config.sessionToken}`);
55
+ }
46
56
  }
47
57
  }
48
58
  else {
@@ -2,11 +2,14 @@ import { BaseProviderConfig, BaseCollectorOptions, ProviderConfigCollector } fro
2
2
  /**
3
3
  * Configuration for AWS Bedrock models.
4
4
  */
5
+ export type BedrockAuthMethod = 'api-key' | 'iam';
5
6
  export interface BedrockConfig extends BaseProviderConfig {
6
7
  type: 'bedrock';
7
8
  region: string;
8
- accessKeyId: string;
9
- secretAccessKey: string;
9
+ authMethod: BedrockAuthMethod;
10
+ apiKey?: string;
11
+ accessKeyId?: string;
12
+ secretAccessKey?: string;
10
13
  sessionToken?: string;
11
14
  modelArn?: string;
12
15
  }
@@ -15,6 +18,8 @@ export interface BedrockConfig extends BaseProviderConfig {
15
18
  */
16
19
  export interface BedrockCollectorOptions extends BaseCollectorOptions {
17
20
  region?: string;
21
+ authMethod?: BedrockAuthMethod;
22
+ apiKey?: string;
18
23
  accessKeyId?: string;
19
24
  secretAccessKey?: string;
20
25
  sessionToken?: string;
@@ -29,56 +29,103 @@ export class BedrockConfigCollector {
29
29
  if (!region) {
30
30
  throw new Error('region is required');
31
31
  }
32
- let accessKeyId = bedrockOptions.accessKeyId;
33
- if (!accessKeyId) {
32
+ let authMethod = bedrockOptions.authMethod;
33
+ if (!authMethod) {
34
34
  const answer = await inquirer.prompt([
35
35
  {
36
- type: 'input',
37
- name: 'accessKeyId',
38
- message: 'AWS access key ID:',
39
- validate: (input) => {
40
- if (!input)
41
- return 'access key ID is required';
42
- return true;
43
- },
36
+ type: 'list',
37
+ name: 'authMethod',
38
+ message: 'Authentication method:',
39
+ choices: [
40
+ { name: 'API key (bearer token)', value: 'api-key' },
41
+ { name: 'IAM credentials', value: 'iam' },
42
+ ],
43
+ default: 'iam',
44
44
  },
45
45
  ]);
46
- accessKeyId = answer.accessKeyId;
46
+ authMethod = answer.authMethod;
47
47
  }
48
- if (!accessKeyId) {
49
- throw new Error('access key ID is required');
48
+ if (!authMethod) {
49
+ throw new Error('authentication method is required');
50
50
  }
51
- let secretAccessKey = bedrockOptions.secretAccessKey;
52
- if (!secretAccessKey) {
53
- const answer = await inquirer.prompt([
54
- {
55
- type: 'password',
56
- name: 'secretAccessKey',
57
- message: 'AWS secret access key:',
58
- mask: '*',
59
- validate: (input) => {
60
- if (!input)
61
- return 'secret access key is required';
62
- return true;
51
+ let apiKey;
52
+ let accessKeyId;
53
+ let secretAccessKey;
54
+ let sessionToken;
55
+ if (authMethod === 'api-key') {
56
+ apiKey = bedrockOptions.apiKey;
57
+ if (!apiKey) {
58
+ const answer = await inquirer.prompt([
59
+ {
60
+ type: 'password',
61
+ name: 'apiKey',
62
+ message: 'Bedrock API key:',
63
+ mask: '*',
64
+ validate: (input) => {
65
+ if (!input)
66
+ return 'API key is required';
67
+ return true;
68
+ },
63
69
  },
64
- },
65
- ]);
66
- secretAccessKey = answer.secretAccessKey;
67
- }
68
- if (!secretAccessKey) {
69
- throw new Error('secret access key is required');
70
+ ]);
71
+ apiKey = answer.apiKey;
72
+ }
73
+ if (!apiKey) {
74
+ throw new Error('API key is required');
75
+ }
70
76
  }
71
- let sessionToken = bedrockOptions.sessionToken;
72
- if (!sessionToken) {
73
- const answer = await inquirer.prompt([
74
- {
75
- type: 'password',
76
- name: 'sessionToken',
77
- message: 'AWS session token (optional, press enter to skip):',
78
- mask: '*',
79
- },
80
- ]);
81
- sessionToken = answer.sessionToken;
77
+ else {
78
+ accessKeyId = bedrockOptions.accessKeyId;
79
+ if (!accessKeyId) {
80
+ const answer = await inquirer.prompt([
81
+ {
82
+ type: 'input',
83
+ name: 'accessKeyId',
84
+ message: 'AWS access key ID:',
85
+ validate: (input) => {
86
+ if (!input)
87
+ return 'access key ID is required';
88
+ return true;
89
+ },
90
+ },
91
+ ]);
92
+ accessKeyId = answer.accessKeyId;
93
+ }
94
+ if (!accessKeyId) {
95
+ throw new Error('access key ID is required');
96
+ }
97
+ secretAccessKey = bedrockOptions.secretAccessKey;
98
+ if (!secretAccessKey) {
99
+ const answer = await inquirer.prompt([
100
+ {
101
+ type: 'password',
102
+ name: 'secretAccessKey',
103
+ message: 'AWS secret access key:',
104
+ mask: '*',
105
+ validate: (input) => {
106
+ if (!input)
107
+ return 'secret access key is required';
108
+ return true;
109
+ },
110
+ },
111
+ ]);
112
+ secretAccessKey = answer.secretAccessKey;
113
+ }
114
+ if (!secretAccessKey) {
115
+ throw new Error('secret access key is required');
116
+ }
117
+ sessionToken = bedrockOptions.sessionToken;
118
+ if (!sessionToken) {
119
+ const answer = await inquirer.prompt([
120
+ {
121
+ type: 'password',
122
+ name: 'sessionToken',
123
+ message: 'AWS session token (optional, press enter to skip):',
124
+ mask: '*',
125
+ },
126
+ ]);
127
+ sessionToken = answer.sessionToken;
128
+ }
82
129
  }
83
130
  let modelArn = bedrockOptions.modelArn;
84
131
  if (!modelArn) {
@@ -96,6 +143,8 @@ export class BedrockConfigCollector {
96
143
  modelValue: options.model,
97
144
  secretName: '',
98
145
  region,
146
+ authMethod,
147
+ apiKey,
99
148
  accessKeyId,
100
149
  secretAccessKey,
101
150
  sessionToken: sessionToken || undefined,
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
- import { executeQuery, parseTarget } from '../../lib/executeQuery.js';
3
+ import { executeQuery, parseTarget, parseParameters, } from '../../lib/executeQuery.js';
4
4
  import { ExitCodes } from '../../lib/errors.js';
5
5
  export function createQueryCommand(config) {
6
6
  const queryCommand = new Command('query');
@@ -10,6 +10,7 @@ export function createQueryCommand(config) {
10
10
  .argument('<message>', 'Message to send')
11
11
  .option('-o, --output <format>', 'Output format: yaml, json, name or events (shows structured event data)')
12
12
  .option('--timeout <timeout>', 'Query timeout (e.g., 30s, 5m, 1h)')
13
+ .option('-p, --parameter <name=value>', 'Template parameter in name=value format (can be used multiple times)', (val, acc) => [...acc, val], [])
13
14
  .option('--session-id <sessionId>', 'Session ID to associate with the query for conversation continuity')
14
15
  .option('--conversation-id <conversationId>', 'Conversation ID to associate with the query for memory continuity')
15
16
  .action(async (target, message, options) => {
@@ -18,12 +19,21 @@ export function createQueryCommand(config) {
18
19
  console.error(chalk.red('Invalid target format. Use: model/name or agent/name etc'));
19
20
  process.exit(ExitCodes.CliError);
20
21
  }
22
+ let parameters;
23
+ try {
24
+ parameters = parseParameters(options.parameter || []);
25
+ }
26
+ catch (error) {
27
+ console.error(chalk.red(error instanceof Error ? error.message : 'Unknown error'));
28
+ process.exit(ExitCodes.CliError);
29
+ }
21
30
  await executeQuery({
22
31
  targetType: parsed.type,
23
32
  targetName: parsed.name,
24
33
  message,
25
34
  outputFormat: options.output,
26
35
  timeout: options.timeout || config.queryTimeout,
36
+ parameters,
27
37
  sessionId: options.sessionId,
28
38
  conversationId: options.conversationId,
29
39
  });
@@ -5,7 +5,7 @@ import { StatusChecker } from '../../components/statusChecker.js';
5
5
  import { StatusFormatter, } from '../../ui/statusFormatter.js';
6
6
  import { fetchVersionInfo } from '../../lib/versions.js';
7
7
  import { waitForServicesReady, } from '../../lib/waitForReady.js';
8
- import { runReadinessChecks, detectStorageBackend, } from '../../lib/readinessChecks.js';
8
+ import { runReadinessChecks, describeStorageBackend, } from '../../lib/readinessChecks.js';
9
9
  import { arkServices } from '../../arkServices.js';
10
10
  import output from '../../lib/output.js';
11
11
  import { parseTimeoutToSeconds } from '../../lib/timeout.js';
@@ -49,7 +49,26 @@ function enrichServiceDetails(service) {
49
49
  details: details.join(', '),
50
50
  };
51
51
  }
52
- function buildStatusSections(data, versionInfo) {
52
+ function backendStatusLine(detection) {
53
+ const display = {
54
+ etcd: { color: 'green', details: 'etcd' },
55
+ postgresql: { color: 'green', details: 'postgresql' },
56
+ 'not-installed': { color: 'yellow', details: 'not installed' },
57
+ unreachable: { color: 'yellow', details: 'cluster unreachable' },
58
+ forbidden: { color: 'yellow', details: 'access denied' },
59
+ undetermined: { color: 'yellow', details: 'undetermined' },
60
+ };
61
+ const { color, details } = display[detection.status];
62
+ return {
63
+ icon: '●',
64
+ iconColor: color,
65
+ status: 'storage backend',
66
+ statusColor: color,
67
+ name: '',
68
+ details,
69
+ };
70
+ }
71
+ function buildStatusSections(data, versionInfo, backend) {
53
72
  const sections = [];
54
73
  // Dependencies section
55
74
  sections.push({
@@ -251,6 +270,9 @@ function buildStatusSections(data, versionInfo) {
251
270
  }
252
271
  }
253
272
  }
273
+ if (backend) {
274
+ arkStatusLines.push(backendStatusLine(backend));
275
+ }
254
276
  sections.push({ title: 'ark status:', lines: arkStatusLines });
255
277
  return sections;
256
278
  }
@@ -266,12 +288,24 @@ export async function checkStatus(serviceNames, options) {
266
288
  statusChecker.checkAll(),
267
289
  fetchVersionInfo(),
268
290
  ]);
291
+ // Only probe for the storage backend if the cluster is reachable; probing an
292
+ // unreachable cluster would just retry to its timeout.
293
+ const detection = statusData.clusterAccess
294
+ ? await describeStorageBackend()
295
+ : {
296
+ backend: 'unknown',
297
+ status: 'unreachable',
298
+ message: 'Cluster is not reachable — cannot determine the storage backend.',
299
+ };
269
300
  spinner.stop();
270
- const sections = buildStatusSections(statusData, versionInfo);
301
+ const sections = buildStatusSections(statusData, versionInfo, detection);
271
302
  StatusFormatter.printSections(sections);
272
303
  if (options?.waitForReady) {
273
304
  const timeoutSeconds = parseTimeoutToSeconds(options.waitForReady);
274
- const backend = await detectStorageBackend();
305
+ const backend = detection.backend;
306
+ if (backend === 'unknown') {
307
+ output.warning(`${detection.message} Skipping backend-specific readiness checks.`);
308
+ }
275
309
  let servicesToWait = [];
276
310
  if (serviceNames && serviceNames.length > 0) {
277
311
  servicesToWait = serviceNames
@@ -316,7 +350,7 @@ export async function checkStatus(serviceNames, options) {
316
350
  waitSpinner.succeed('All services are ready');
317
351
  const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
318
352
  const remainingSeconds = Math.max(1, timeoutSeconds - elapsedSeconds);
319
- const deepResults = await runReadinessChecks(remainingSeconds, (r) => {
353
+ const deepResults = await runReadinessChecks(remainingSeconds, backend, (r) => {
320
354
  const icon = r.passed ? chalk.green('✓') : chalk.red('✗');
321
355
  const dur = `${(r.durationMs / 1000).toFixed(1)}s`;
322
356
  const suffix = r.message ? ` — ${r.message}` : '';
@@ -58,6 +58,10 @@ export declare class ArkApiClient {
58
58
  sessionId?: string;
59
59
  conversationId?: string;
60
60
  timeout?: string;
61
+ parameters?: Array<{
62
+ name: string;
63
+ value?: string;
64
+ }>;
61
65
  metadata?: {
62
66
  annotations?: Record<string, string>;
63
67
  };
@@ -159,6 +159,9 @@ export class ArkApiClient {
159
159
  sessionId: params.sessionId,
160
160
  conversationId: params.conversationId,
161
161
  timeout: params.timeout,
162
+ ...(params.parameters && params.parameters.length > 0
163
+ ? { parameters: params.parameters }
164
+ : {}),
162
165
  ...(params.metadata ? { metadata: params.metadata } : {}),
163
166
  }),
164
167
  });
@@ -1,4 +1,5 @@
1
1
  import { ArkApiClient, QueryTarget } from './arkApiClient.js';
2
+ import type { QueryParameter } from './types.js';
2
3
  export { QueryTarget };
3
4
  export interface ChatConfig {
4
5
  streamingEnabled: boolean;
@@ -7,6 +8,7 @@ export interface ChatConfig {
7
8
  conversationId?: string;
8
9
  queryTimeout?: string;
9
10
  a2aContextId?: string;
11
+ parameters?: QueryParameter[];
10
12
  }
11
13
  export interface ToolCall {
12
14
  id: string;