@blogic-cz/agent-tools 0.14.3 → 0.14.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blogic-cz/agent-tools",
3
- "version": "0.14.3",
3
+ "version": "0.14.5",
4
4
  "description": "CLI tools for AI coding agent workflows — GitHub, database, Kubernetes, Azure DevOps, logs, sessions, and audit",
5
5
  "keywords": [
6
6
  "agent",
@@ -76,6 +76,7 @@ export class DbService extends Context.Service<
76
76
  const remotePort = dbConfig.remotePort ?? 5432;
77
77
 
78
78
  const zshrcEnvCache = yield* Ref.make<Record<string, string> | null>(null);
79
+ const envTemplateRegex = /^\$\{([A-Za-z0-9_]+)\}$/;
79
80
 
80
81
  const loadEnvFromZshrc = Effect.fn("DbService.loadEnvFromZshrc")(function* () {
81
82
  const cached = yield* Ref.get(zshrcEnvCache);
@@ -141,6 +142,42 @@ export class DbService extends Context.Service<
141
142
  return "";
142
143
  });
143
144
 
145
+ const resolveConfigString = Effect.fn("DbService.resolveConfigString")(function* (
146
+ value: string,
147
+ env: string,
148
+ label: string,
149
+ zshrcEnv: Record<string, string>,
150
+ ) {
151
+ const match = value.match(envTemplateRegex);
152
+ if (!match) return value;
153
+
154
+ const envVar = match[1];
155
+ const fromEnv = Bun.env[envVar];
156
+ if (fromEnv !== undefined) return fromEnv;
157
+
158
+ const fromZsh = zshrcEnv[envVar];
159
+ if (fromZsh !== undefined) return fromZsh;
160
+
161
+ return yield* new DbConnectionError({
162
+ message: `Environment variable ${envVar} (required for '${label}' config field) is not set in environment ${env}.`,
163
+ environment: env,
164
+ });
165
+ });
166
+
167
+ const resolveDbConfig = Effect.fn("DbService.resolveDbConfig")(function* (
168
+ config: DbConfig,
169
+ env: string,
170
+ ) {
171
+ const needsEnvResolution =
172
+ envTemplateRegex.test(config.user) || envTemplateRegex.test(config.database);
173
+ const zshrcEnv = needsEnvResolution ? yield* loadEnvFromZshrc() : {};
174
+ return {
175
+ ...config,
176
+ user: yield* resolveConfigString(config.user, env, "user", zshrcEnv),
177
+ database: yield* resolveConfigString(config.database, env, "database", zshrcEnv),
178
+ };
179
+ });
180
+
144
181
  const executeShellCommand = (command: ChildProcess.Command) =>
145
182
  Effect.scoped(
146
183
  Effect.gen(function* () {
@@ -183,6 +220,7 @@ export class DbService extends Context.Service<
183
220
  dbConfig,
184
221
  (command, _label) => executeShellCommand(command),
185
222
  effect,
223
+ { tryWithoutPrerequisites: true },
186
224
  ).pipe(
187
225
  Effect.mapError((error) =>
188
226
  isPrerequisiteRunError(error)
@@ -583,10 +621,11 @@ export class DbService extends Context.Service<
583
621
  ) {
584
622
  const config = getConfigForEnv(env);
585
623
  const startTimeMs = yield* Clock.currentTimeMillis;
586
- const password = yield* resolvePassword(config, env);
624
+ const resolvedConfig = yield* resolveDbConfig(config, env);
625
+ const password = yield* resolvePassword(resolvedConfig, env);
587
626
  const mutation = isMutationQuery(sql);
588
627
 
589
- if (mutation && !config.allowMutations) {
628
+ if (mutation && !resolvedConfig.allowMutations) {
590
629
  return yield* new DbMutationBlockedError({
591
630
  message:
592
631
  "Mutation queries (UPDATE, INSERT, DELETE, etc.) are not allowed on this environment. Use a local environment for mutations.",
@@ -595,12 +634,12 @@ export class DbService extends Context.Service<
595
634
  }
596
635
 
597
636
  const queryEffect = mutation
598
- ? executeMutationQuery(config, sql, password, Number(startTimeMs))
599
- : executeSelectQuery(config, sql, password, Number(startTimeMs), true);
637
+ ? executeMutationQuery(resolvedConfig, sql, password, Number(startTimeMs))
638
+ : executeSelectQuery(resolvedConfig, sql, password, Number(startTimeMs), true);
600
639
 
601
640
  return yield* runWithVpnPrerequisites(
602
- config.port,
603
- runQueryWithOptionalTunnel(config, queryEffect),
641
+ resolvedConfig.port,
642
+ runQueryWithOptionalTunnel(resolvedConfig, queryEffect),
604
643
  );
605
644
  });
606
645
 
@@ -611,7 +650,8 @@ export class DbService extends Context.Service<
611
650
  ) {
612
651
  const config = getConfigForEnv(env);
613
652
  const startTimeMs = yield* Clock.currentTimeMillis;
614
- const password = yield* resolvePassword(config, env);
653
+ const resolvedConfig = yield* resolveDbConfig(config, env);
654
+ const password = yield* resolvePassword(resolvedConfig, env);
615
655
 
616
656
  if (mode === "columns" && !table) {
617
657
  const endTime = yield* Clock.currentTimeMillis;
@@ -636,16 +676,26 @@ export class DbService extends Context.Service<
636
676
 
637
677
  const queryEffect =
638
678
  mode === "tables"
639
- ? executeSelectQuery(config, getTableNames(), password, Number(startTimeMs))
679
+ ? executeSelectQuery(resolvedConfig, getTableNames(), password, Number(startTimeMs))
640
680
  : mode === "columns"
641
- ? executeSelectQuery(config, getColumns(table ?? ""), password, Number(startTimeMs))
681
+ ? executeSelectQuery(
682
+ resolvedConfig,
683
+ getColumns(table ?? ""),
684
+ password,
685
+ Number(startTimeMs),
686
+ )
642
687
  : mode === "relationships"
643
- ? executeSelectQuery(config, getRelationships(), password, Number(startTimeMs))
644
- : executeFullSchemaQuery(config, password, Number(startTimeMs));
688
+ ? executeSelectQuery(
689
+ resolvedConfig,
690
+ getRelationships(),
691
+ password,
692
+ Number(startTimeMs),
693
+ )
694
+ : executeFullSchemaQuery(resolvedConfig, password, Number(startTimeMs));
645
695
 
646
696
  const result = yield* runWithVpnPrerequisites(
647
- config.port,
648
- runQueryWithOptionalTunnel(config, queryEffect),
697
+ resolvedConfig.port,
698
+ runQueryWithOptionalTunnel(resolvedConfig, queryEffect),
649
699
  );
650
700
 
651
701
  if (result.success) {
@@ -220,6 +220,7 @@ export class K8sService extends Context.Service<
220
220
  command: fullCommand,
221
221
  };
222
222
  }),
223
+ { tryWithoutPrerequisites: true },
223
224
  ).pipe(
224
225
  Effect.mapError((error) =>
225
226
  isPrerequisiteRunError(error)
@@ -116,6 +116,7 @@ export const runWithProfilePrerequisites = <A, E, CommandError>(
116
116
  profile: ProfilePrerequisites,
117
117
  runCommand: PrerequisiteCommandRunner<CommandError>,
118
118
  effect: Effect.Effect<A, E, never>,
119
+ options?: { tryWithoutPrerequisites?: boolean },
119
120
  ): Effect.Effect<A, E | PrerequisiteRunError, never> => {
120
121
  const prerequisites = normalizeProfilePrerequisites(profile);
121
122
  const vpnPrerequisites = prerequisites.filter((prerequisite) => prerequisite.type === "vpn");
@@ -125,6 +126,13 @@ export const runWithProfilePrerequisites = <A, E, CommandError>(
125
126
  }
126
127
 
127
128
  return Effect.gen(function* () {
129
+ if (options?.tryWithoutPrerequisites) {
130
+ const directResult = yield* effect.pipe(Effect.result);
131
+ if (Result.isSuccess(directResult)) {
132
+ return directResult.success;
133
+ }
134
+ }
135
+
128
136
  const startedDrivers: Array<{ driver: ResolvedVpnDriver; cooldownMs: number }> = [];
129
137
 
130
138
  for (const prerequisite of vpnPrerequisites) {