@blogic-cz/agent-tools 1.5.2 → 1.5.4
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 +4 -4
- package/dist/audit-tool/main.d.ts +3 -0
- package/dist/audit-tool/main.d.ts.map +1 -0
- package/dist/az-tool/main.d.ts +3 -0
- package/dist/az-tool/main.d.ts.map +1 -0
- package/dist/azdo-tool/main.d.ts +3 -0
- package/dist/azdo-tool/main.d.ts.map +1 -0
- package/dist/credential-guard/index.d.ts.map +1 -1
- package/dist/db-tool/main.d.ts +3 -0
- package/dist/db-tool/main.d.ts.map +1 -0
- package/dist/gh-tool/api.d.ts.map +1 -1
- package/dist/gh-tool/gist.d.ts.map +1 -1
- package/dist/gh-tool/main.d.ts +3 -0
- package/dist/gh-tool/main.d.ts.map +1 -0
- package/dist/gh-tool/pr/core.d.ts.map +1 -1
- package/dist/gh-tool/service.d.ts.map +1 -1
- package/dist/gh-tool/text-input.d.ts +4 -0
- package/dist/gh-tool/text-input.d.ts.map +1 -1
- package/dist/gh-tool/workflow.d.ts.map +1 -1
- package/dist/k8s-tool/main.d.ts +3 -0
- package/dist/k8s-tool/main.d.ts.map +1 -0
- package/dist/logs-tool/index.d.ts +1 -19
- package/dist/logs-tool/index.d.ts.map +1 -1
- package/dist/logs-tool/main.d.ts +21 -0
- package/dist/logs-tool/main.d.ts.map +1 -0
- package/dist/observability-tool/main.d.ts +3 -0
- package/dist/observability-tool/main.d.ts.map +1 -0
- package/dist/session-tool/index.d.ts +1 -11
- package/dist/session-tool/index.d.ts.map +1 -1
- package/dist/session-tool/main.d.ts +13 -0
- package/dist/session-tool/main.d.ts.map +1 -0
- package/dist/shared/effect-version.d.ts +4 -0
- package/dist/shared/effect-version.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/audit-tool/index.ts +3 -109
- package/src/audit-tool/main.ts +111 -0
- package/src/az-tool/index.ts +3 -151
- package/src/az-tool/main.ts +153 -0
- package/src/azdo-tool/index.ts +3 -220
- package/src/azdo-tool/main.ts +222 -0
- package/src/credential-guard/index.ts +102 -4
- package/src/db-tool/index.ts +3 -260
- package/src/db-tool/main.ts +262 -0
- package/src/gh-tool/api.ts +21 -1
- package/src/gh-tool/gist.ts +7 -1
- package/src/gh-tool/index.ts +3 -256
- package/src/gh-tool/main.ts +258 -0
- package/src/gh-tool/pr/core.ts +52 -2
- package/src/gh-tool/service.ts +9 -5
- package/src/gh-tool/text-input.ts +63 -4
- package/src/gh-tool/workflow.ts +1 -0
- package/src/k8s-tool/index.ts +3 -487
- package/src/k8s-tool/main.ts +489 -0
- package/src/logs-tool/index.ts +3 -247
- package/src/logs-tool/main.ts +249 -0
- package/src/observability-tool/index.ts +3 -34
- package/src/observability-tool/main.ts +36 -0
- package/src/session-tool/index.ts +3 -337
- package/src/session-tool/main.ts +339 -0
- package/src/shared/effect-version.ts +32 -0
package/src/logs-tool/index.ts
CHANGED
|
@@ -1,249 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import { checkEffectVersion } from "#shared/effect-version";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* Reads application logs from local development or test/prod environments.
|
|
7
|
-
* For test/prod, uses k8s-tool internally to access logs from PVC.
|
|
8
|
-
*
|
|
9
|
-
* Run with --help for full usage documentation.
|
|
10
|
-
*
|
|
11
|
-
* IMPORTANT FOR AI AGENTS:
|
|
12
|
-
* Use this tool to investigate application behavior and errors.
|
|
13
|
-
* Always start with --list to see available log files.
|
|
14
|
-
* Use --format toon for LLM-optimized output (fewer tokens).
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { Command, Flag } from "effect/unstable/cli";
|
|
18
|
-
import { BunRuntime, BunServices } from "@effect/platform-bun";
|
|
19
|
-
import { Effect, Layer, Option, Result } from "effect";
|
|
20
|
-
|
|
21
|
-
import type { Environment, LogResult, ReadOptions } from "./types";
|
|
22
|
-
|
|
23
|
-
import {
|
|
24
|
-
makeSchemaCommand,
|
|
25
|
-
formatOption,
|
|
26
|
-
formatOutput,
|
|
27
|
-
logText,
|
|
28
|
-
renderCauseToStderr,
|
|
29
|
-
VERSION,
|
|
30
|
-
} from "#shared";
|
|
31
|
-
import { AuditServiceLayer, withAudit } from "#shared/audit";
|
|
32
|
-
import { ConfigService, ConfigServiceLayer, getDefaultEnvironment } from "#config";
|
|
33
|
-
import { LogsConfigError, LogsNotFoundError, LogsReadError, LogsTimeoutError } from "./errors";
|
|
34
|
-
import { LogsService, LogsServiceLayer } from "./service";
|
|
35
|
-
|
|
36
|
-
const profileOption = Flag.optional(
|
|
37
|
-
Flag.String("profile").pipe(
|
|
38
|
-
Flag.withDescription(
|
|
39
|
-
"Named profile from agent-tools.json5 logs section (default: 'default' key or single entry)",
|
|
40
|
-
),
|
|
41
|
-
),
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Resolve environment from explicit --env flag, config defaultEnvironment, or fail with hint.
|
|
46
|
-
*/
|
|
47
|
-
const resolveEnv = (envOption: Option.Option<string>) =>
|
|
48
|
-
Effect.gen(function* () {
|
|
49
|
-
const explicit = Option.getOrUndefined(envOption);
|
|
50
|
-
if (explicit) return explicit;
|
|
51
|
-
|
|
52
|
-
const config = yield* ConfigService;
|
|
53
|
-
const defaultEnv = getDefaultEnvironment(config);
|
|
54
|
-
|
|
55
|
-
if (defaultEnv === "prod") {
|
|
56
|
-
return yield* new LogsConfigError({
|
|
57
|
-
message:
|
|
58
|
-
"Implicit prod access blocked. Config defaultEnvironment is 'prod' but --env was not passed explicitly.",
|
|
59
|
-
hint: "Pass --env prod explicitly to confirm production access, or change defaultEnvironment to a non-prod value.",
|
|
60
|
-
nextCommand: "agent-tools-logs list --env prod",
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (defaultEnv) return defaultEnv;
|
|
65
|
-
|
|
66
|
-
return yield* new LogsConfigError({
|
|
67
|
-
message:
|
|
68
|
-
"No environment specified. Use --env <name> or set defaultEnvironment in agent-tools.json5.",
|
|
69
|
-
hint: 'Set defaultEnvironment in agent-tools.json5 (e.g. defaultEnvironment: "local") or pass --env explicitly.',
|
|
70
|
-
nextCommand: "agent-tools-logs list --env local",
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const buildSource = (
|
|
75
|
-
mode: "list" | "read",
|
|
76
|
-
env: Environment,
|
|
77
|
-
logsConfig: { localDir: string; remotePath: string } | undefined,
|
|
78
|
-
options: ReadOptions | null,
|
|
79
|
-
): string | undefined => {
|
|
80
|
-
if (!logsConfig) return undefined;
|
|
81
|
-
|
|
82
|
-
if (mode === "list") {
|
|
83
|
-
return env === "local" ? logsConfig.localDir : `${env}:${logsConfig.remotePath}`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (env === "local") {
|
|
87
|
-
const fileName = options?.file ?? "latest";
|
|
88
|
-
return `${logsConfig.localDir}/${fileName}`;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const fileName = options?.file ?? "app.log";
|
|
92
|
-
return `${env}:${logsConfig.remotePath}/${fileName}`;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const listCommand = Command.make(
|
|
96
|
-
"list",
|
|
97
|
-
{
|
|
98
|
-
env: Flag.optional(Flag.String("env")).pipe(
|
|
99
|
-
Flag.withDescription(
|
|
100
|
-
"Target environment (e.g. local, test, prod). Falls back to defaultEnvironment in config.",
|
|
101
|
-
),
|
|
102
|
-
),
|
|
103
|
-
format: formatOption,
|
|
104
|
-
profile: profileOption,
|
|
105
|
-
},
|
|
106
|
-
({ env, format, profile }) =>
|
|
107
|
-
Effect.gen(function* () {
|
|
108
|
-
const resolvedEnv = yield* resolveEnv(env);
|
|
109
|
-
const logsService = yield* LogsService;
|
|
110
|
-
const startTime = Date.now();
|
|
111
|
-
const profileName = Option.getOrUndefined(profile);
|
|
112
|
-
|
|
113
|
-
const result = yield* logsService
|
|
114
|
-
.listLogs(resolvedEnv as Environment, profileName)
|
|
115
|
-
.pipe(Effect.result);
|
|
116
|
-
const executionTimeMs = Date.now() - startTime;
|
|
117
|
-
|
|
118
|
-
const logResult: LogResult = Result.match(result, {
|
|
119
|
-
onFailure: (error) => ({
|
|
120
|
-
success: false,
|
|
121
|
-
error: error.message,
|
|
122
|
-
source: error instanceof LogsNotFoundError ? error.path : undefined,
|
|
123
|
-
executionTimeMs,
|
|
124
|
-
hint:
|
|
125
|
-
error.hint ??
|
|
126
|
-
(error instanceof LogsReadError && error.source === "config"
|
|
127
|
-
? "Add a 'logs' section to agent-tools.json5 with localDir and remotePath."
|
|
128
|
-
: undefined),
|
|
129
|
-
nextCommand:
|
|
130
|
-
error.nextCommand ??
|
|
131
|
-
(error instanceof LogsReadError && error.source === "config"
|
|
132
|
-
? "agent-tools-logs list --env local"
|
|
133
|
-
: undefined),
|
|
134
|
-
retryable: error.retryable ?? (error instanceof LogsTimeoutError ? true : undefined),
|
|
135
|
-
}),
|
|
136
|
-
onSuccess: (data) => ({
|
|
137
|
-
success: true,
|
|
138
|
-
data,
|
|
139
|
-
source: buildSource("list", resolvedEnv as Environment, undefined, null),
|
|
140
|
-
executionTimeMs,
|
|
141
|
-
}),
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
yield* logText(formatOutput(logResult, format));
|
|
145
|
-
}),
|
|
146
|
-
).pipe(Command.withDescription("List available log files"));
|
|
147
|
-
|
|
148
|
-
const readCommand = Command.make(
|
|
149
|
-
"read",
|
|
150
|
-
{
|
|
151
|
-
env: Flag.optional(Flag.String("env")).pipe(
|
|
152
|
-
Flag.withDescription(
|
|
153
|
-
"Target environment (e.g. local, test, prod). Falls back to defaultEnvironment in config.",
|
|
154
|
-
),
|
|
155
|
-
),
|
|
156
|
-
file: Flag.String("file").pipe(
|
|
157
|
-
Flag.withDescription("Specific log file to read"),
|
|
158
|
-
Flag.optional,
|
|
159
|
-
),
|
|
160
|
-
format: formatOption,
|
|
161
|
-
grep: Flag.String("grep").pipe(
|
|
162
|
-
Flag.withDescription("Filter lines containing case-insensitive literal text"),
|
|
163
|
-
Flag.optional,
|
|
164
|
-
),
|
|
165
|
-
pretty: Flag.Boolean("pretty").pipe(
|
|
166
|
-
Flag.withDescription("Pretty-print JSON log entries"),
|
|
167
|
-
Flag.withDefault(false),
|
|
168
|
-
),
|
|
169
|
-
profile: profileOption,
|
|
170
|
-
tail: Flag.Int("tail").pipe(Flag.withDescription("Show last N lines"), Flag.withDefault(100)),
|
|
171
|
-
},
|
|
172
|
-
({ env, file, format, grep, pretty, profile, tail }) =>
|
|
173
|
-
Effect.gen(function* () {
|
|
174
|
-
const resolvedEnv = yield* resolveEnv(env);
|
|
175
|
-
const logsService = yield* LogsService;
|
|
176
|
-
const startTime = Date.now();
|
|
177
|
-
const profileName = Option.getOrUndefined(profile);
|
|
178
|
-
|
|
179
|
-
const readOptions: ReadOptions = {
|
|
180
|
-
tail,
|
|
181
|
-
grep: Option.getOrUndefined(grep),
|
|
182
|
-
file: Option.getOrUndefined(file),
|
|
183
|
-
pretty,
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const result = yield* logsService
|
|
187
|
-
.readLogs(resolvedEnv as Environment, readOptions, profileName)
|
|
188
|
-
.pipe(Effect.result);
|
|
189
|
-
const executionTimeMs = Date.now() - startTime;
|
|
190
|
-
|
|
191
|
-
const logResult: LogResult = Result.match(result, {
|
|
192
|
-
onFailure: (error) => ({
|
|
193
|
-
success: false,
|
|
194
|
-
error: error.message,
|
|
195
|
-
source: error instanceof LogsNotFoundError ? error.path : undefined,
|
|
196
|
-
executionTimeMs,
|
|
197
|
-
hint:
|
|
198
|
-
error.hint ??
|
|
199
|
-
(error instanceof LogsReadError && error.source === "config"
|
|
200
|
-
? "Add a 'logs' section to agent-tools.json5 with localDir and remotePath."
|
|
201
|
-
: undefined),
|
|
202
|
-
nextCommand:
|
|
203
|
-
error.nextCommand ??
|
|
204
|
-
(error instanceof LogsReadError && error.source === "config"
|
|
205
|
-
? "agent-tools-logs read --env local --file app.log"
|
|
206
|
-
: undefined),
|
|
207
|
-
retryable: error.retryable ?? (error instanceof LogsTimeoutError ? true : undefined),
|
|
208
|
-
}),
|
|
209
|
-
onSuccess: (data) => ({
|
|
210
|
-
success: true,
|
|
211
|
-
data,
|
|
212
|
-
source: buildSource("read", resolvedEnv as Environment, undefined, readOptions),
|
|
213
|
-
executionTimeMs,
|
|
214
|
-
}),
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
yield* logText(formatOutput(logResult, format));
|
|
218
|
-
}),
|
|
219
|
-
).pipe(Command.withDescription("Read application logs"));
|
|
220
|
-
|
|
221
|
-
const commandsCommand = makeSchemaCommand(() => mainCommand);
|
|
222
|
-
|
|
223
|
-
const mainCommand = Command.make("logs-tool", {}).pipe(
|
|
224
|
-
Command.withDescription("Application Logs Tool for Coding Agents"),
|
|
225
|
-
Command.withSubcommands([listCommand, readCommand, commandsCommand]),
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
const cli = Command.run(mainCommand, {
|
|
229
|
-
version: VERSION,
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
export const run = Command.runWith(mainCommand, {
|
|
233
|
-
version: VERSION,
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
const MainLayer = LogsServiceLayer.pipe(
|
|
237
|
-
Layer.provideMerge(ConfigServiceLayer),
|
|
238
|
-
Layer.provideMerge(BunServices.layer),
|
|
239
|
-
Layer.provideMerge(AuditServiceLayer),
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
const program = withAudit("logs", cli).pipe(
|
|
243
|
-
Effect.provide(MainLayer),
|
|
244
|
-
Effect.tapCause(renderCauseToStderr),
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
BunRuntime.runMain(program, {
|
|
248
|
-
disableErrorReporting: true,
|
|
249
|
-
});
|
|
4
|
+
if (await checkEffectVersion()) await import("./main");
|
|
5
|
+
else process.exitCode = 1;
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Application Logs Tool for Coding Agents
|
|
5
|
+
*
|
|
6
|
+
* Reads application logs from local development or test/prod environments.
|
|
7
|
+
* For test/prod, uses k8s-tool internally to access logs from PVC.
|
|
8
|
+
*
|
|
9
|
+
* Run with --help for full usage documentation.
|
|
10
|
+
*
|
|
11
|
+
* IMPORTANT FOR AI AGENTS:
|
|
12
|
+
* Use this tool to investigate application behavior and errors.
|
|
13
|
+
* Always start with --list to see available log files.
|
|
14
|
+
* Use --format toon for LLM-optimized output (fewer tokens).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
18
|
+
import { BunRuntime, BunServices } from "@effect/platform-bun";
|
|
19
|
+
import { Effect, Layer, Option, Result } from "effect";
|
|
20
|
+
|
|
21
|
+
import type { Environment, LogResult, ReadOptions } from "./types";
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
makeSchemaCommand,
|
|
25
|
+
formatOption,
|
|
26
|
+
formatOutput,
|
|
27
|
+
logText,
|
|
28
|
+
renderCauseToStderr,
|
|
29
|
+
VERSION,
|
|
30
|
+
} from "#shared";
|
|
31
|
+
import { AuditServiceLayer, withAudit } from "#shared/audit";
|
|
32
|
+
import { ConfigService, ConfigServiceLayer, getDefaultEnvironment } from "#config";
|
|
33
|
+
import { LogsConfigError, LogsNotFoundError, LogsReadError, LogsTimeoutError } from "./errors";
|
|
34
|
+
import { LogsService, LogsServiceLayer } from "./service";
|
|
35
|
+
|
|
36
|
+
const profileOption = Flag.optional(
|
|
37
|
+
Flag.String("profile").pipe(
|
|
38
|
+
Flag.withDescription(
|
|
39
|
+
"Named profile from agent-tools.json5 logs section (default: 'default' key or single entry)",
|
|
40
|
+
),
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolve environment from explicit --env flag, config defaultEnvironment, or fail with hint.
|
|
46
|
+
*/
|
|
47
|
+
const resolveEnv = (envOption: Option.Option<string>) =>
|
|
48
|
+
Effect.gen(function* () {
|
|
49
|
+
const explicit = Option.getOrUndefined(envOption);
|
|
50
|
+
if (explicit) return explicit;
|
|
51
|
+
|
|
52
|
+
const config = yield* ConfigService;
|
|
53
|
+
const defaultEnv = getDefaultEnvironment(config);
|
|
54
|
+
|
|
55
|
+
if (defaultEnv === "prod") {
|
|
56
|
+
return yield* new LogsConfigError({
|
|
57
|
+
message:
|
|
58
|
+
"Implicit prod access blocked. Config defaultEnvironment is 'prod' but --env was not passed explicitly.",
|
|
59
|
+
hint: "Pass --env prod explicitly to confirm production access, or change defaultEnvironment to a non-prod value.",
|
|
60
|
+
nextCommand: "agent-tools-logs list --env prod",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (defaultEnv) return defaultEnv;
|
|
65
|
+
|
|
66
|
+
return yield* new LogsConfigError({
|
|
67
|
+
message:
|
|
68
|
+
"No environment specified. Use --env <name> or set defaultEnvironment in agent-tools.json5.",
|
|
69
|
+
hint: 'Set defaultEnvironment in agent-tools.json5 (e.g. defaultEnvironment: "local") or pass --env explicitly.',
|
|
70
|
+
nextCommand: "agent-tools-logs list --env local",
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const buildSource = (
|
|
75
|
+
mode: "list" | "read",
|
|
76
|
+
env: Environment,
|
|
77
|
+
logsConfig: { localDir: string; remotePath: string } | undefined,
|
|
78
|
+
options: ReadOptions | null,
|
|
79
|
+
): string | undefined => {
|
|
80
|
+
if (!logsConfig) return undefined;
|
|
81
|
+
|
|
82
|
+
if (mode === "list") {
|
|
83
|
+
return env === "local" ? logsConfig.localDir : `${env}:${logsConfig.remotePath}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (env === "local") {
|
|
87
|
+
const fileName = options?.file ?? "latest";
|
|
88
|
+
return `${logsConfig.localDir}/${fileName}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const fileName = options?.file ?? "app.log";
|
|
92
|
+
return `${env}:${logsConfig.remotePath}/${fileName}`;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const listCommand = Command.make(
|
|
96
|
+
"list",
|
|
97
|
+
{
|
|
98
|
+
env: Flag.optional(Flag.String("env")).pipe(
|
|
99
|
+
Flag.withDescription(
|
|
100
|
+
"Target environment (e.g. local, test, prod). Falls back to defaultEnvironment in config.",
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
format: formatOption,
|
|
104
|
+
profile: profileOption,
|
|
105
|
+
},
|
|
106
|
+
({ env, format, profile }) =>
|
|
107
|
+
Effect.gen(function* () {
|
|
108
|
+
const resolvedEnv = yield* resolveEnv(env);
|
|
109
|
+
const logsService = yield* LogsService;
|
|
110
|
+
const startTime = Date.now();
|
|
111
|
+
const profileName = Option.getOrUndefined(profile);
|
|
112
|
+
|
|
113
|
+
const result = yield* logsService
|
|
114
|
+
.listLogs(resolvedEnv as Environment, profileName)
|
|
115
|
+
.pipe(Effect.result);
|
|
116
|
+
const executionTimeMs = Date.now() - startTime;
|
|
117
|
+
|
|
118
|
+
const logResult: LogResult = Result.match(result, {
|
|
119
|
+
onFailure: (error) => ({
|
|
120
|
+
success: false,
|
|
121
|
+
error: error.message,
|
|
122
|
+
source: error instanceof LogsNotFoundError ? error.path : undefined,
|
|
123
|
+
executionTimeMs,
|
|
124
|
+
hint:
|
|
125
|
+
error.hint ??
|
|
126
|
+
(error instanceof LogsReadError && error.source === "config"
|
|
127
|
+
? "Add a 'logs' section to agent-tools.json5 with localDir and remotePath."
|
|
128
|
+
: undefined),
|
|
129
|
+
nextCommand:
|
|
130
|
+
error.nextCommand ??
|
|
131
|
+
(error instanceof LogsReadError && error.source === "config"
|
|
132
|
+
? "agent-tools-logs list --env local"
|
|
133
|
+
: undefined),
|
|
134
|
+
retryable: error.retryable ?? (error instanceof LogsTimeoutError ? true : undefined),
|
|
135
|
+
}),
|
|
136
|
+
onSuccess: (data) => ({
|
|
137
|
+
success: true,
|
|
138
|
+
data,
|
|
139
|
+
source: buildSource("list", resolvedEnv as Environment, undefined, null),
|
|
140
|
+
executionTimeMs,
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
yield* logText(formatOutput(logResult, format));
|
|
145
|
+
}),
|
|
146
|
+
).pipe(Command.withDescription("List available log files"));
|
|
147
|
+
|
|
148
|
+
const readCommand = Command.make(
|
|
149
|
+
"read",
|
|
150
|
+
{
|
|
151
|
+
env: Flag.optional(Flag.String("env")).pipe(
|
|
152
|
+
Flag.withDescription(
|
|
153
|
+
"Target environment (e.g. local, test, prod). Falls back to defaultEnvironment in config.",
|
|
154
|
+
),
|
|
155
|
+
),
|
|
156
|
+
file: Flag.String("file").pipe(
|
|
157
|
+
Flag.withDescription("Specific log file to read"),
|
|
158
|
+
Flag.optional,
|
|
159
|
+
),
|
|
160
|
+
format: formatOption,
|
|
161
|
+
grep: Flag.String("grep").pipe(
|
|
162
|
+
Flag.withDescription("Filter lines containing case-insensitive literal text"),
|
|
163
|
+
Flag.optional,
|
|
164
|
+
),
|
|
165
|
+
pretty: Flag.Boolean("pretty").pipe(
|
|
166
|
+
Flag.withDescription("Pretty-print JSON log entries"),
|
|
167
|
+
Flag.withDefault(false),
|
|
168
|
+
),
|
|
169
|
+
profile: profileOption,
|
|
170
|
+
tail: Flag.Int("tail").pipe(Flag.withDescription("Show last N lines"), Flag.withDefault(100)),
|
|
171
|
+
},
|
|
172
|
+
({ env, file, format, grep, pretty, profile, tail }) =>
|
|
173
|
+
Effect.gen(function* () {
|
|
174
|
+
const resolvedEnv = yield* resolveEnv(env);
|
|
175
|
+
const logsService = yield* LogsService;
|
|
176
|
+
const startTime = Date.now();
|
|
177
|
+
const profileName = Option.getOrUndefined(profile);
|
|
178
|
+
|
|
179
|
+
const readOptions: ReadOptions = {
|
|
180
|
+
tail,
|
|
181
|
+
grep: Option.getOrUndefined(grep),
|
|
182
|
+
file: Option.getOrUndefined(file),
|
|
183
|
+
pretty,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const result = yield* logsService
|
|
187
|
+
.readLogs(resolvedEnv as Environment, readOptions, profileName)
|
|
188
|
+
.pipe(Effect.result);
|
|
189
|
+
const executionTimeMs = Date.now() - startTime;
|
|
190
|
+
|
|
191
|
+
const logResult: LogResult = Result.match(result, {
|
|
192
|
+
onFailure: (error) => ({
|
|
193
|
+
success: false,
|
|
194
|
+
error: error.message,
|
|
195
|
+
source: error instanceof LogsNotFoundError ? error.path : undefined,
|
|
196
|
+
executionTimeMs,
|
|
197
|
+
hint:
|
|
198
|
+
error.hint ??
|
|
199
|
+
(error instanceof LogsReadError && error.source === "config"
|
|
200
|
+
? "Add a 'logs' section to agent-tools.json5 with localDir and remotePath."
|
|
201
|
+
: undefined),
|
|
202
|
+
nextCommand:
|
|
203
|
+
error.nextCommand ??
|
|
204
|
+
(error instanceof LogsReadError && error.source === "config"
|
|
205
|
+
? "agent-tools-logs read --env local --file app.log"
|
|
206
|
+
: undefined),
|
|
207
|
+
retryable: error.retryable ?? (error instanceof LogsTimeoutError ? true : undefined),
|
|
208
|
+
}),
|
|
209
|
+
onSuccess: (data) => ({
|
|
210
|
+
success: true,
|
|
211
|
+
data,
|
|
212
|
+
source: buildSource("read", resolvedEnv as Environment, undefined, readOptions),
|
|
213
|
+
executionTimeMs,
|
|
214
|
+
}),
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
yield* logText(formatOutput(logResult, format));
|
|
218
|
+
}),
|
|
219
|
+
).pipe(Command.withDescription("Read application logs"));
|
|
220
|
+
|
|
221
|
+
const commandsCommand = makeSchemaCommand(() => mainCommand);
|
|
222
|
+
|
|
223
|
+
const mainCommand = Command.make("logs-tool", {}).pipe(
|
|
224
|
+
Command.withDescription("Application Logs Tool for Coding Agents"),
|
|
225
|
+
Command.withSubcommands([listCommand, readCommand, commandsCommand]),
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const cli = Command.run(mainCommand, {
|
|
229
|
+
version: VERSION,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
export const run = Command.runWith(mainCommand, {
|
|
233
|
+
version: VERSION,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const MainLayer = LogsServiceLayer.pipe(
|
|
237
|
+
Layer.provideMerge(ConfigServiceLayer),
|
|
238
|
+
Layer.provideMerge(BunServices.layer),
|
|
239
|
+
Layer.provideMerge(AuditServiceLayer),
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const program = withAudit("logs", cli).pipe(
|
|
243
|
+
Effect.provide(MainLayer),
|
|
244
|
+
Effect.tapCause(renderCauseToStderr),
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
BunRuntime.runMain(program, {
|
|
248
|
+
disableErrorReporting: true,
|
|
249
|
+
});
|
|
@@ -1,36 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import { checkEffectVersion } from "#shared/effect-version";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import type { Cause } from "effect";
|
|
6
|
-
import { Command } from "effect/unstable/cli";
|
|
7
|
-
|
|
8
|
-
import { ConfigServiceLayer } from "#config";
|
|
9
|
-
import { AuditServiceLayer, withAudit } from "#shared/audit";
|
|
10
|
-
import { makeSchemaCommand, VERSION } from "#shared";
|
|
11
|
-
|
|
12
|
-
import { metricsCommand } from "./metrics";
|
|
13
|
-
import { logsCommand } from "./logs";
|
|
14
|
-
import { traceCommand } from "./trace";
|
|
15
|
-
|
|
16
|
-
const renderCauseToStderr = (cause: Cause.Cause<unknown>) => Console.error(cause.toString());
|
|
17
|
-
|
|
18
|
-
const commandsCommand = makeSchemaCommand(() => mainCommand);
|
|
19
|
-
|
|
20
|
-
const mainCommand = Command.make("observability-tool", {}).pipe(
|
|
21
|
-
Command.withDescription(
|
|
22
|
-
"LGTM observability queries — Tempo traces, Loki logs, Prometheus metrics",
|
|
23
|
-
),
|
|
24
|
-
Command.withSubcommands([traceCommand, metricsCommand, logsCommand, commandsCommand]),
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const cli = Command.run(mainCommand, { version: VERSION });
|
|
28
|
-
|
|
29
|
-
const MainLayer = Layer.mergeAll(BunServices.layer, ConfigServiceLayer, AuditServiceLayer);
|
|
30
|
-
|
|
31
|
-
const program = withAudit("observability", cli).pipe(
|
|
32
|
-
Effect.provide(MainLayer),
|
|
33
|
-
Effect.tapCause(renderCauseToStderr),
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
BunRuntime.runMain(program, { disableErrorReporting: true });
|
|
4
|
+
if (await checkEffectVersion()) await import("./main");
|
|
5
|
+
else process.exitCode = 1;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { BunRuntime, BunServices } from "@effect/platform-bun";
|
|
4
|
+
import { Console, Effect, Layer } from "effect";
|
|
5
|
+
import type { Cause } from "effect";
|
|
6
|
+
import { Command } from "effect/unstable/cli";
|
|
7
|
+
|
|
8
|
+
import { ConfigServiceLayer } from "#config";
|
|
9
|
+
import { AuditServiceLayer, withAudit } from "#shared/audit";
|
|
10
|
+
import { makeSchemaCommand, VERSION } from "#shared";
|
|
11
|
+
|
|
12
|
+
import { metricsCommand } from "./metrics";
|
|
13
|
+
import { logsCommand } from "./logs";
|
|
14
|
+
import { traceCommand } from "./trace";
|
|
15
|
+
|
|
16
|
+
const renderCauseToStderr = (cause: Cause.Cause<unknown>) => Console.error(cause.toString());
|
|
17
|
+
|
|
18
|
+
const commandsCommand = makeSchemaCommand(() => mainCommand);
|
|
19
|
+
|
|
20
|
+
const mainCommand = Command.make("observability-tool", {}).pipe(
|
|
21
|
+
Command.withDescription(
|
|
22
|
+
"LGTM observability queries — Tempo traces, Loki logs, Prometheus metrics",
|
|
23
|
+
),
|
|
24
|
+
Command.withSubcommands([traceCommand, metricsCommand, logsCommand, commandsCommand]),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const cli = Command.run(mainCommand, { version: VERSION });
|
|
28
|
+
|
|
29
|
+
const MainLayer = Layer.mergeAll(BunServices.layer, ConfigServiceLayer, AuditServiceLayer);
|
|
30
|
+
|
|
31
|
+
const program = withAudit("observability", cli).pipe(
|
|
32
|
+
Effect.provide(MainLayer),
|
|
33
|
+
Effect.tapCause(renderCauseToStderr),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
BunRuntime.runMain(program, { disableErrorReporting: true });
|