@blogic-cz/agent-tools 0.14.20 → 0.14.22
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/README.md +33 -0
- package/package.json +1 -1
- package/schemas/agent-tools.schema.json +11 -0
- package/src/config/loader.ts +2 -0
- package/src/config/types.ts +1 -1
- package/src/db-tool/service.ts +7 -1
- package/src/db-tool/types.ts +2 -1
- package/src/gh-tool/pr/commands.ts +3 -1
- package/src/gh-tool/pr/core.ts +10 -5
- package/src/gh-tool/types.ts +4 -0
- package/src/shared/prerequisites/config.ts +19 -0
package/README.md
CHANGED
|
@@ -383,6 +383,39 @@ Secrets are **never** stored in the config file. The `db-tool` config references
|
|
|
383
383
|
}
|
|
384
384
|
```
|
|
385
385
|
|
|
386
|
+
Database VPN prerequisites can be set at the database profile or environment level. If an environment declares `vpn` or `prerequisites`, that environment config replaces the profile prerequisites; `prerequisites: []` explicitly disables inherited VPN setup. DB commands try the query directly first and only connect VPN prerequisites if direct access fails.
|
|
387
|
+
|
|
388
|
+
```json5
|
|
389
|
+
{
|
|
390
|
+
vpns: {
|
|
391
|
+
officeVpn: { name: "OfficeVPN" },
|
|
392
|
+
prodVpn: { name: "ProdVPN" },
|
|
393
|
+
},
|
|
394
|
+
database: {
|
|
395
|
+
default: {
|
|
396
|
+
vpn: "officeVpn",
|
|
397
|
+
environments: {
|
|
398
|
+
local: {
|
|
399
|
+
host: "127.0.0.1",
|
|
400
|
+
port: 5432,
|
|
401
|
+
user: "app",
|
|
402
|
+
database: "app",
|
|
403
|
+
prerequisites: [], // no VPN for local/direct access
|
|
404
|
+
},
|
|
405
|
+
prod: {
|
|
406
|
+
host: "db.prod.internal",
|
|
407
|
+
port: 5432,
|
|
408
|
+
user: "readonly",
|
|
409
|
+
database: "app",
|
|
410
|
+
passwordEnvVar: "AGENT_TOOLS_DB_PROD_PASSWORD",
|
|
411
|
+
vpn: "prodVpn", // overrides database.default.vpn
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
386
419
|
Set the values in your shell:
|
|
387
420
|
|
|
388
421
|
```bash
|
package/package.json
CHANGED
|
@@ -185,6 +185,17 @@
|
|
|
185
185
|
"passwordEnvVar": {
|
|
186
186
|
"description": "Name of the environment variable holding the database password.",
|
|
187
187
|
"type": "string"
|
|
188
|
+
},
|
|
189
|
+
"prerequisites": {
|
|
190
|
+
"description": "Ordered prerequisite references for this database environment. Declaring this or vpn overrides profile-level database prerequisites; an empty array explicitly disables inherited prerequisites.",
|
|
191
|
+
"type": "array",
|
|
192
|
+
"items": {
|
|
193
|
+
"$ref": "#/definitions/Prerequisite"
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"vpn": {
|
|
197
|
+
"type": "string",
|
|
198
|
+
"description": "Convenience sugar for this database environment's VPN prerequisite key. Declaring this or prerequisites overrides profile-level database prerequisites."
|
|
188
199
|
}
|
|
189
200
|
},
|
|
190
201
|
"required": ["host", "port", "user", "database"]
|
package/src/config/loader.ts
CHANGED
|
@@ -91,6 +91,8 @@ const DbEnvConfigSchema = Schema.Struct({
|
|
|
91
91
|
database: Schema.String,
|
|
92
92
|
password: Schema.optionalKey(Schema.String),
|
|
93
93
|
passwordEnvVar: Schema.optionalKey(Schema.String),
|
|
94
|
+
prerequisites: Schema.optionalKey(PrerequisitesSchema),
|
|
95
|
+
vpn: Schema.optionalKey(Schema.String),
|
|
94
96
|
});
|
|
95
97
|
|
|
96
98
|
const DatabaseConfigSchema = Schema.Struct({
|
package/src/config/types.ts
CHANGED
package/src/db-tool/service.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { DbConfig, DbMutationOperation, QueryResult, SchemaMode } from "./t
|
|
|
6
6
|
import { ConfigService } from "#config";
|
|
7
7
|
import { isPrerequisiteRunError } from "#shared/prerequisites/errors";
|
|
8
8
|
import { resolveEnvTemplate } from "#shared/env-template";
|
|
9
|
+
import { resolveEnvironmentScopedPrerequisites } from "#shared/prerequisites/config";
|
|
9
10
|
import { runWithProfilePrerequisites } from "#shared/prerequisites/runtime";
|
|
10
11
|
import { DbConfigService, DbConfigServiceLayer, TUNNEL_CHECK_INTERVAL_MS } from "./config-service";
|
|
11
12
|
import {
|
|
@@ -228,13 +229,15 @@ export class DbService extends Context.Service<
|
|
|
228
229
|
|
|
229
230
|
const runWithVpnPrerequisites = <E>(
|
|
230
231
|
port: number,
|
|
232
|
+
prerequisiteConfig: DbConfig,
|
|
231
233
|
effect: Effect.Effect<QueryResult, E>,
|
|
232
234
|
): Effect.Effect<QueryResult, E | DbTunnelError> =>
|
|
233
235
|
runWithProfilePrerequisites(
|
|
234
236
|
agentToolsConfig ?? {},
|
|
235
|
-
|
|
237
|
+
prerequisiteConfig,
|
|
236
238
|
(command, _label) => executeShellCommand(command),
|
|
237
239
|
effect,
|
|
240
|
+
{ tryWithoutPrerequisites: true },
|
|
238
241
|
).pipe(
|
|
239
242
|
Effect.mapError((error) =>
|
|
240
243
|
isPrerequisiteRunError(error)
|
|
@@ -655,6 +658,7 @@ export class DbService extends Context.Service<
|
|
|
655
658
|
database: envConfig.database,
|
|
656
659
|
password: envConfig.password,
|
|
657
660
|
passwordEnvVar: envConfig.passwordEnvVar,
|
|
661
|
+
...resolveEnvironmentScopedPrerequisites(dbConfig, envConfig),
|
|
658
662
|
port: envConfig.port,
|
|
659
663
|
needsTunnel: accessMode.needsTunnel,
|
|
660
664
|
allowMutations: accessMode.allowMutations,
|
|
@@ -696,6 +700,7 @@ export class DbService extends Context.Service<
|
|
|
696
700
|
|
|
697
701
|
return yield* runWithVpnPrerequisites(
|
|
698
702
|
resolvedConfig.port,
|
|
703
|
+
resolvedConfig,
|
|
699
704
|
runQueryWithOptionalTunnel(resolvedConfig, queryEffect),
|
|
700
705
|
);
|
|
701
706
|
});
|
|
@@ -752,6 +757,7 @@ export class DbService extends Context.Service<
|
|
|
752
757
|
|
|
753
758
|
const result = yield* runWithVpnPrerequisites(
|
|
754
759
|
resolvedConfig.port,
|
|
760
|
+
resolvedConfig,
|
|
755
761
|
runQueryWithOptionalTunnel(resolvedConfig, queryEffect),
|
|
756
762
|
);
|
|
757
763
|
|
package/src/db-tool/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DbMutationOperation } from "#config";
|
|
2
|
+
import type { ProfilePrerequisites } from "#config/types";
|
|
2
3
|
import type { Environment, OutputFormat } from "#shared";
|
|
3
4
|
|
|
4
5
|
export type { DbMutationOperation };
|
|
@@ -6,7 +7,7 @@ export type { Environment, OutputFormat };
|
|
|
6
7
|
|
|
7
8
|
export type SchemaMode = "tables" | "columns" | "full" | "relationships";
|
|
8
9
|
|
|
9
|
-
export type DbConfig = {
|
|
10
|
+
export type DbConfig = ProfilePrerequisites & {
|
|
10
11
|
host: string;
|
|
11
12
|
user: string;
|
|
12
13
|
database: string;
|
|
@@ -119,6 +119,7 @@ export const prCreateCommand = Command.make(
|
|
|
119
119
|
export const prEditCommand = Command.make(
|
|
120
120
|
"edit",
|
|
121
121
|
{
|
|
122
|
+
base: Flag.string("base").pipe(Flag.withDescription("New base branch"), Flag.optional),
|
|
122
123
|
body: Flag.string("body").pipe(Flag.withDescription("New PR body/description"), Flag.optional),
|
|
123
124
|
bodyFile: Flag.string("body-file").pipe(
|
|
124
125
|
Flag.withDescription("Read PR body from a file path or '-' for stdin"),
|
|
@@ -128,7 +129,7 @@ export const prEditCommand = Command.make(
|
|
|
128
129
|
pr: Flag.integer("pr").pipe(Flag.withDescription("PR number to edit")),
|
|
129
130
|
title: Flag.string("title").pipe(Flag.withDescription("New PR title"), Flag.optional),
|
|
130
131
|
},
|
|
131
|
-
({ body, bodyFile, format, pr, title }) =>
|
|
132
|
+
({ base, body, bodyFile, format, pr, title }) =>
|
|
132
133
|
Effect.gen(function* () {
|
|
133
134
|
const resolvedBody = yield* resolveOptionalTextInput(
|
|
134
135
|
"gh-tool pr edit",
|
|
@@ -143,6 +144,7 @@ export const prEditCommand = Command.make(
|
|
|
143
144
|
pr,
|
|
144
145
|
title: Option.getOrNull(title),
|
|
145
146
|
body: resolvedBody,
|
|
147
|
+
base: Option.getOrNull(base),
|
|
146
148
|
});
|
|
147
149
|
yield* logFormatted(info, format);
|
|
148
150
|
}),
|
package/src/gh-tool/pr/core.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
MergeResult,
|
|
9
9
|
MergeStrategy,
|
|
10
10
|
PRInfo,
|
|
11
|
+
PRViewInfo,
|
|
11
12
|
WorkflowRunDetail,
|
|
12
13
|
} from "#gh/types";
|
|
13
14
|
|
|
@@ -243,9 +244,9 @@ export const viewPR = Effect.fn("pr.viewPR")(function* (prNumber: number | null)
|
|
|
243
244
|
if (prNumber !== null) {
|
|
244
245
|
args.push(String(prNumber));
|
|
245
246
|
}
|
|
246
|
-
args.push("--json", "number,url,title,headRefName,baseRefName,state,isDraft,mergeable");
|
|
247
|
+
args.push("--json", "number,url,title,headRefName,baseRefName,state,isDraft,mergeable,body");
|
|
247
248
|
|
|
248
|
-
const info = yield* gh.runGhJson<
|
|
249
|
+
const info = yield* gh.runGhJson<PRViewInfo>(args);
|
|
249
250
|
return info;
|
|
250
251
|
});
|
|
251
252
|
|
|
@@ -632,14 +633,15 @@ export const editPR = Effect.fn("pr.editPR")(function* (opts: {
|
|
|
632
633
|
pr: number;
|
|
633
634
|
title: string | null;
|
|
634
635
|
body: string | null;
|
|
636
|
+
base: string | null;
|
|
635
637
|
}) {
|
|
636
|
-
if (!opts.title && !opts.body) {
|
|
638
|
+
if (!opts.title && !opts.body && !opts.base) {
|
|
637
639
|
return yield* Effect.fail(
|
|
638
640
|
new GitHubCommandError({
|
|
639
641
|
command: "pr edit",
|
|
640
642
|
exitCode: 1,
|
|
641
|
-
stderr: "At least one of --title or --
|
|
642
|
-
message: "At least one of --title or --
|
|
643
|
+
stderr: "At least one of --title, --body, or --base must be provided",
|
|
644
|
+
message: "At least one of --title, --body, or --base must be provided",
|
|
643
645
|
}),
|
|
644
646
|
);
|
|
645
647
|
}
|
|
@@ -654,6 +656,9 @@ export const editPR = Effect.fn("pr.editPR")(function* (opts: {
|
|
|
654
656
|
if (opts.body) {
|
|
655
657
|
editArgs.push("--body", opts.body);
|
|
656
658
|
}
|
|
659
|
+
if (opts.base) {
|
|
660
|
+
editArgs.push("--base", opts.base);
|
|
661
|
+
}
|
|
657
662
|
|
|
658
663
|
yield* gh.runGh(editArgs);
|
|
659
664
|
|
package/src/gh-tool/types.ts
CHANGED
|
@@ -36,3 +36,22 @@ export function resolveProfilePrerequisites(
|
|
|
36
36
|
|
|
37
37
|
return { success: true, prerequisites };
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
const hasOwnPrerequisiteConfig = (profile: ProfilePrerequisites, key: keyof ProfilePrerequisites) =>
|
|
41
|
+
Object.prototype.hasOwnProperty.call(profile, key);
|
|
42
|
+
|
|
43
|
+
export function resolveEnvironmentScopedPrerequisites(
|
|
44
|
+
profile: ProfilePrerequisites,
|
|
45
|
+
environment: ProfilePrerequisites,
|
|
46
|
+
): ProfilePrerequisites {
|
|
47
|
+
const source =
|
|
48
|
+
hasOwnPrerequisiteConfig(environment, "vpn") ||
|
|
49
|
+
hasOwnPrerequisiteConfig(environment, "prerequisites")
|
|
50
|
+
? environment
|
|
51
|
+
: profile;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
...(source.vpn !== undefined ? { vpn: source.vpn } : {}),
|
|
55
|
+
...(source.prerequisites !== undefined ? { prerequisites: source.prerequisites } : {}),
|
|
56
|
+
};
|
|
57
|
+
}
|