@agents-at-scale/ark 0.1.62 → 0.1.63

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.
@@ -6,5 +6,6 @@ export declare function installArk(config: ArkConfig, serviceNames?: string[], o
6
6
  verbose?: boolean;
7
7
  arkVersion?: string;
8
8
  marketplaceVersion?: string;
9
+ backend?: string;
9
10
  }): Promise<void>;
10
11
  export declare function createInstallCommand(config: ArkConfig): Command;
@@ -10,7 +10,43 @@ import { printNextSteps } from '../../lib/nextSteps.js';
10
10
  import ora from 'ora';
11
11
  import { waitForServicesReady, } from '../../lib/waitForReady.js';
12
12
  import { parseTimeoutToSeconds } from '../../lib/timeout.js';
13
- import { detectStorageBackend } from '../../lib/readinessChecks.js';
13
+ import { runReadinessChecks } from '../../lib/readinessChecks.js';
14
+ function validatePostgresConfig(pg) {
15
+ if (!pg) {
16
+ throw new Error("missing 'storage.postgresql' block in .arkrc.yaml");
17
+ }
18
+ for (const key of ['host', 'user', 'passwordSecretName']) {
19
+ if (!pg[key]) {
20
+ throw new Error(`missing required field storage.postgresql.${key} in .arkrc.yaml`);
21
+ }
22
+ }
23
+ return pg;
24
+ }
25
+ function backendInstallArgs(service, backend, values) {
26
+ if (backend === 'etcd')
27
+ return [];
28
+ if (!values)
29
+ return [];
30
+ if (service.helmReleaseName === 'ark-controller') {
31
+ return ['--set', 'storage.backend=postgresql'];
32
+ }
33
+ if (service.helmReleaseName === 'ark-apiserver') {
34
+ const args = [];
35
+ args.push('--set', `postgresql.host=${values.host}`);
36
+ if (values.port !== undefined)
37
+ args.push('--set', `postgresql.port=${values.port}`);
38
+ if (values.database)
39
+ args.push('--set', `postgresql.database=${values.database}`);
40
+ args.push('--set', `postgresql.user=${values.user}`);
41
+ args.push('--set', `postgresql.passwordSecretName=${values.passwordSecretName}`);
42
+ if (values.passwordSecretKey)
43
+ args.push('--set', `postgresql.passwordSecretKey=${values.passwordSecretKey}`);
44
+ if (values.sslMode)
45
+ args.push('--set', `postgresql.sslMode=${values.sslMode}`);
46
+ return args;
47
+ }
48
+ return [];
49
+ }
14
50
  function isValidVersion(version) {
15
51
  return /^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version);
16
52
  }
@@ -76,7 +112,7 @@ async function checkAndCleanFailedRelease(releaseName, namespace, verbose = fals
76
112
  // Ignore errors - prerequisite may not exist
77
113
  }
78
114
  }
79
- async function installService(service, verbose = false, arkVersionOverride, marketplaceVersionOverride, backend) {
115
+ async function installService(service, verbose = false, arkVersionOverride, marketplaceVersionOverride, extraArgs) {
80
116
  await uninstallPrerequisites(service, verbose);
81
117
  await checkAndCleanFailedRelease(service.helmReleaseName, service.namespace, verbose);
82
118
  let chartPath = service.chartPath;
@@ -112,6 +148,8 @@ async function installService(service, verbose = false, arkVersionOverride, mark
112
148
  }
113
149
  // Add any additional install args
114
150
  helmArgs.push(...(service.installArgs || []));
151
+ if (extraArgs)
152
+ helmArgs.push(...extraArgs);
115
153
  await execute('helm', helmArgs, {
116
154
  stdout: 'inherit',
117
155
  stderr: 'pipe',
@@ -141,7 +179,22 @@ export async function installArk(config, serviceNames = [], options = {}) {
141
179
  // Show cluster info
142
180
  output.success(`connected to cluster: ${chalk.bold(clusterInfo.context)}`);
143
181
  console.log(); // Add blank line after cluster info
144
- const backend = await detectStorageBackend();
182
+ const requestedBackend = options.backend ?? config.storage?.backend ?? 'etcd';
183
+ if (requestedBackend !== 'etcd' && requestedBackend !== 'postgresql') {
184
+ output.error(`Invalid backend value: ${requestedBackend}. Expected 'etcd' or 'postgresql'.`);
185
+ process.exit(1);
186
+ }
187
+ const backend = requestedBackend;
188
+ let postgresValues;
189
+ if (backend === 'postgresql') {
190
+ try {
191
+ postgresValues = validatePostgresConfig(config.storage?.postgresql);
192
+ }
193
+ catch (err) {
194
+ output.error(`${err instanceof Error ? err.message : String(err)}`);
195
+ process.exit(1);
196
+ }
197
+ }
145
198
  // If specific services are requested, install only those services
146
199
  if (serviceNames.length > 0) {
147
200
  for (const serviceName of serviceNames) {
@@ -176,7 +229,7 @@ export async function installArk(config, serviceNames = [], options = {}) {
176
229
  }
177
230
  output.info(`installing marketplace item ${service.name}...`);
178
231
  try {
179
- await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion);
232
+ await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion, []);
180
233
  output.success(`${service.name} installed successfully`);
181
234
  }
182
235
  catch (error) {
@@ -199,8 +252,26 @@ export async function installArk(config, serviceNames = [], options = {}) {
199
252
  }
200
253
  output.info(`installing ${service.name}...`);
201
254
  try {
202
- await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion);
255
+ await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion, backendInstallArgs(service, backend, postgresValues));
203
256
  output.success(`${service.name} installed successfully`);
257
+ // Wait for ark-apiserver to be ready before continuing to other services
258
+ if (service.helmReleaseName === 'ark-apiserver' && backend === 'postgresql') {
259
+ const spinner = ora('Waiting for ark-apiserver to be ready...').start();
260
+ try {
261
+ const results = await runReadinessChecks(120); // 2 minute timeout
262
+ const failed = results.find((r) => !r.passed);
263
+ if (failed) {
264
+ spinner.fail(`ark-apiserver readiness check failed: ${failed.message || 'unknown error'}`);
265
+ output.error('ark-apiserver is not ready. Stopping installation.');
266
+ process.exit(1);
267
+ }
268
+ spinner.succeed('ark-apiserver is ready');
269
+ }
270
+ catch (error) {
271
+ spinner.fail('Failed to check ark-apiserver readiness');
272
+ throw error;
273
+ }
274
+ }
204
275
  }
205
276
  catch (error) {
206
277
  if (handleInstallError(error, service, options)) {
@@ -341,7 +412,25 @@ export async function installArk(config, serviceNames = [], options = {}) {
341
412
  }
342
413
  output.info(`installing ${service.name}...`);
343
414
  try {
344
- await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion);
415
+ await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion, backendInstallArgs(service, backend, postgresValues));
416
+ // Wait for ark-apiserver to be ready before continuing to other services
417
+ if (service.helmReleaseName === 'ark-apiserver' && backend === 'postgresql') {
418
+ const spinner = ora('Waiting for ark-apiserver to be ready...').start();
419
+ try {
420
+ const results = await runReadinessChecks(120); // 2 minute timeout
421
+ const failed = results.find((r) => !r.passed);
422
+ if (failed) {
423
+ spinner.fail(`ark-apiserver readiness check failed: ${failed.message || 'unknown error'}`);
424
+ output.error('ark-apiserver is not ready. Stopping installation.');
425
+ process.exit(1);
426
+ }
427
+ spinner.succeed('ark-apiserver is ready');
428
+ }
429
+ catch (error) {
430
+ spinner.fail('Failed to check ark-apiserver readiness');
431
+ throw error;
432
+ }
433
+ }
345
434
  console.log(); // Add blank line after command output
346
435
  }
347
436
  catch (error) {
@@ -383,7 +472,7 @@ export async function installArk(config, serviceNames = [], options = {}) {
383
472
  for (const service of sortedServices) {
384
473
  output.info(`installing ${service.name}...`);
385
474
  try {
386
- await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion);
475
+ await installService(service, options.verbose, options.arkVersion, options.marketplaceVersion, backendInstallArgs(service, backend, postgresValues));
387
476
  console.log(); // Add blank line after command output
388
477
  }
389
478
  catch (error) {
@@ -447,6 +536,7 @@ export function createInstallCommand(config) {
447
536
  .option('--ark-version <version>', 'ARK version to install (e.g., 0.1.50, defaults to CLI version)')
448
537
  .option('--marketplace-version <version>', 'Marketplace item version to install (e.g., 0.1.5)')
449
538
  .option('--wait-for-ready <timeout>', 'wait for Ark to be ready after installation (e.g., 30s, 2m)')
539
+ .option('--backend <type>', "storage backend: 'etcd' (default) or 'postgresql' (overrides storage.backend in .arkrc.yaml)")
450
540
  .option('-v, --verbose', 'show commands being executed')
451
541
  .action(async (services, options) => {
452
542
  await installArk(config, services, options);
@@ -8,6 +8,19 @@ export interface MarketplaceConfig {
8
8
  repoUrl?: string;
9
9
  registry?: string;
10
10
  }
11
+ export interface PostgresStorageConfig {
12
+ host: string;
13
+ port?: number | string;
14
+ database?: string;
15
+ user: string;
16
+ passwordSecretName: string;
17
+ passwordSecretKey?: string;
18
+ sslMode?: string;
19
+ }
20
+ export interface StorageConfig {
21
+ backend?: 'etcd' | 'postgresql';
22
+ postgresql?: PostgresStorageConfig;
23
+ }
11
24
  export interface ArkConfig {
12
25
  chat?: ChatConfig;
13
26
  marketplace?: MarketplaceConfig;
@@ -15,6 +28,7 @@ export interface ArkConfig {
15
28
  reusePortForwards?: boolean;
16
29
  [serviceName: string]: Partial<ArkService> | boolean | undefined;
17
30
  };
31
+ storage?: StorageConfig;
18
32
  queryTimeout?: string;
19
33
  defaultExportTypes?: string[];
20
34
  clusterInfo?: ClusterInfo;
@@ -80,6 +80,13 @@ export function loadConfig() {
80
80
  config.services.reusePortForwards =
81
81
  process.env.ARK_SERVICES_REUSE_PORT_FORWARDS === '1';
82
82
  }
83
+ if (process.env.ARK_STORAGE_BACKEND !== undefined) {
84
+ const backend = process.env.ARK_STORAGE_BACKEND;
85
+ if (backend === 'etcd' || backend === 'postgresql') {
86
+ config.storage = config.storage || {};
87
+ config.storage.backend = backend;
88
+ }
89
+ }
83
90
  return config;
84
91
  }
85
92
  /**
@@ -119,6 +126,18 @@ function mergeConfig(target, source) {
119
126
  }
120
127
  }
121
128
  }
129
+ if (source.storage) {
130
+ target.storage = target.storage || {};
131
+ if (source.storage.backend !== undefined) {
132
+ target.storage.backend = source.storage.backend;
133
+ }
134
+ if (source.storage.postgresql) {
135
+ target.storage.postgresql = {
136
+ ...target.storage.postgresql,
137
+ ...source.storage.postgresql,
138
+ };
139
+ }
140
+ }
122
141
  if (source.queryTimeout !== undefined) {
123
142
  target.queryTimeout = source.queryTimeout;
124
143
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-at-scale/ark",
3
- "version": "0.1.62",
3
+ "version": "0.1.63",
4
4
  "description": "Ark CLI - Interactive terminal interface for ARK agents",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -87,7 +87,7 @@
87
87
  "overrides": {
88
88
  "minimatch": "^10.2.3",
89
89
  "rollup": "4.59.0",
90
- "hono": "^4.12.14",
90
+ "hono": "^4.12.18",
91
91
  "@hono/node-server": "^1.19.13",
92
92
  "flatted": "^3.4.2",
93
93
  "tar": "^7.5.11",
@@ -772,11 +772,11 @@ wheels = [
772
772
 
773
773
  [[package]]
774
774
  name = "python-multipart"
775
- version = "0.0.26"
775
+ version = "0.0.27"
776
776
  source = { registry = "https://pypi.org/simple" }
777
- sdist = { url = "https://files.pythonhosted.org/packages/88/71/b145a380824a960ebd60e1014256dbb7d2253f2316ff2d73dfd8928ec2c3/python_multipart-0.0.26.tar.gz", hash = "sha256:08fadc45918cd615e26846437f50c5d6d23304da32c341f289a617127b081f17", size = 43501, upload-time = "2026-04-10T14:09:59.473Z" }
777
+ sdist = { url = "https://files.pythonhosted.org/packages/69/9b/f23807317a113dc36e74e75eb265a02dd1a4d9082abc3c1064acd22997c4/python_multipart-0.0.27.tar.gz", hash = "sha256:9870a6a8c5a20a5bf4f07c017bd1489006ff8836cff097b6933355ee2b49b602", size = 44043, upload-time = "2026-04-27T10:51:26.649Z" }
778
778
  wheels = [
779
- { url = "https://files.pythonhosted.org/packages/9a/22/f1925cdda983ab66fc8ec6ec8014b959262747e58bdca26a4e3d1da29d56/python_multipart-0.0.26-py3-none-any.whl", hash = "sha256:c0b169f8c4484c13b0dcf2ef0ec3a4adb255c4b7d18d8e420477d2b1dd03f185", size = 28847, upload-time = "2026-04-10T14:09:58.131Z" },
779
+ { url = "https://files.pythonhosted.org/packages/99/78/4126abcbdbd3c559d43e0db7f7b9173fc6befe45d39a2856cc0b8ec2a5a6/python_multipart-0.0.27-py3-none-any.whl", hash = "sha256:6fccfad17a27334bd0193681b369f476eda3409f17381a2d65aa7df3f7275645", size = 29254, upload-time = "2026-04-27T10:51:24.997Z" },
780
780
  ]
781
781
 
782
782
  [[package]]