@blogic-cz/agent-tools 0.16.0 → 0.17.1
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/dist/audit-tool/index.d.ts +3 -0
- package/dist/audit-tool/index.d.ts.map +1 -0
- package/dist/az-tool/build.d.ts +47 -0
- package/dist/az-tool/build.d.ts.map +1 -0
- package/dist/az-tool/config.d.ts +13 -0
- package/dist/az-tool/config.d.ts.map +1 -0
- package/dist/az-tool/errors.d.ts +43 -0
- package/dist/az-tool/errors.d.ts.map +1 -0
- package/dist/az-tool/extract-option-value.d.ts +2 -0
- package/dist/az-tool/extract-option-value.d.ts.map +1 -0
- package/dist/az-tool/index.d.ts +3 -0
- package/dist/az-tool/index.d.ts.map +1 -0
- package/dist/az-tool/security.d.ts +4 -0
- package/dist/az-tool/security.d.ts.map +1 -0
- package/dist/az-tool/service.d.ts +15 -0
- package/dist/az-tool/service.d.ts.map +1 -0
- package/dist/az-tool/transformers.d.ts +10 -0
- package/dist/az-tool/transformers.d.ts.map +1 -0
- package/dist/az-tool/types.d.ts +64 -0
- package/dist/az-tool/types.d.ts.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/loader.d.ts +22 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/types.d.ts +189 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/credential-guard/claude-hook.d.ts +13 -0
- package/dist/credential-guard/claude-hook.d.ts.map +1 -0
- package/dist/credential-guard/index.d.ts +77 -0
- package/dist/credential-guard/index.d.ts.map +1 -0
- package/dist/db-tool/config-service.d.ts +25 -0
- package/dist/db-tool/config-service.d.ts.map +1 -0
- package/dist/db-tool/errors.d.ts +41 -0
- package/dist/db-tool/errors.d.ts.map +1 -0
- package/dist/db-tool/index.d.ts +3 -0
- package/dist/db-tool/index.d.ts.map +1 -0
- package/dist/db-tool/schema.d.ts +21 -0
- package/dist/db-tool/schema.d.ts.map +1 -0
- package/dist/db-tool/security.d.ts +23 -0
- package/dist/db-tool/security.d.ts.map +1 -0
- package/dist/db-tool/service.d.ts +18 -0
- package/dist/db-tool/service.d.ts.map +1 -0
- package/dist/db-tool/sql-client.d.ts +24 -0
- package/dist/db-tool/sql-client.d.ts.map +1 -0
- package/dist/db-tool/transformers.d.ts +10 -0
- package/dist/db-tool/transformers.d.ts.map +1 -0
- package/dist/db-tool/types.d.ts +37 -0
- package/dist/db-tool/types.d.ts.map +1 -0
- package/dist/gh-tool/branch.d.ts +18 -0
- package/dist/gh-tool/branch.d.ts.map +1 -0
- package/dist/gh-tool/config.d.ts +7 -0
- package/dist/gh-tool/config.d.ts.map +1 -0
- package/dist/gh-tool/errors.d.ts +51 -0
- package/dist/gh-tool/errors.d.ts.map +1 -0
- package/dist/gh-tool/index.d.ts +3 -0
- package/dist/gh-tool/index.d.ts.map +1 -0
- package/dist/gh-tool/issue/commands.d.ts +68 -0
- package/dist/gh-tool/issue/commands.d.ts.map +1 -0
- package/dist/gh-tool/issue/core.d.ts +82 -0
- package/dist/gh-tool/issue/core.d.ts.map +1 -0
- package/dist/gh-tool/issue/index.d.ts +3 -0
- package/dist/gh-tool/issue/index.d.ts.map +1 -0
- package/dist/gh-tool/issue/triage.d.ts +89 -0
- package/dist/gh-tool/issue/triage.d.ts.map +1 -0
- package/dist/gh-tool/pr/commands.d.ts +384 -0
- package/dist/gh-tool/pr/commands.d.ts.map +1 -0
- package/dist/gh-tool/pr/core.d.ts +236 -0
- package/dist/gh-tool/pr/core.d.ts.map +1 -0
- package/dist/gh-tool/pr/helpers.d.ts +27 -0
- package/dist/gh-tool/pr/helpers.d.ts.map +1 -0
- package/dist/gh-tool/pr/index.d.ts +2 -0
- package/dist/gh-tool/pr/index.d.ts.map +1 -0
- package/dist/gh-tool/pr/review.d.ts +90 -0
- package/dist/gh-tool/pr/review.d.ts.map +1 -0
- package/dist/gh-tool/release.d.ts +52 -0
- package/dist/gh-tool/release.d.ts.map +1 -0
- package/dist/gh-tool/repo.d.ts +21 -0
- package/dist/gh-tool/repo.d.ts.map +1 -0
- package/dist/gh-tool/service.d.ts +25 -0
- package/dist/gh-tool/service.d.ts.map +1 -0
- package/dist/gh-tool/text-input.d.ts +22 -0
- package/dist/gh-tool/text-input.d.ts.map +1 -0
- package/dist/gh-tool/types.d.ts +271 -0
- package/dist/gh-tool/types.d.ts.map +1 -0
- package/dist/gh-tool/workflow.d.ts +157 -0
- package/dist/gh-tool/workflow.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/k8s-tool/context.d.ts +26 -0
- package/dist/k8s-tool/context.d.ts.map +1 -0
- package/dist/k8s-tool/errors.d.ts +43 -0
- package/dist/k8s-tool/errors.d.ts.map +1 -0
- package/dist/k8s-tool/index.d.ts +3 -0
- package/dist/k8s-tool/index.d.ts.map +1 -0
- package/dist/k8s-tool/profile.d.ts +3 -0
- package/dist/k8s-tool/profile.d.ts.map +1 -0
- package/dist/k8s-tool/security.d.ts +14 -0
- package/dist/k8s-tool/security.d.ts.map +1 -0
- package/dist/k8s-tool/service.d.ts +16 -0
- package/dist/k8s-tool/service.d.ts.map +1 -0
- package/dist/k8s-tool/transformers.d.ts +23 -0
- package/dist/k8s-tool/transformers.d.ts.map +1 -0
- package/dist/k8s-tool/types.d.ts +13 -0
- package/dist/k8s-tool/types.d.ts.map +1 -0
- package/dist/logs-tool/errors.d.ts +40 -0
- package/dist/logs-tool/errors.d.ts.map +1 -0
- package/dist/logs-tool/index.d.ts +21 -0
- package/dist/logs-tool/index.d.ts.map +1 -0
- package/dist/logs-tool/service.d.ts +17 -0
- package/dist/logs-tool/service.d.ts.map +1 -0
- package/dist/logs-tool/transformers.d.ts +2 -0
- package/dist/logs-tool/transformers.d.ts.map +1 -0
- package/dist/logs-tool/types.d.ts +39 -0
- package/dist/logs-tool/types.d.ts.map +1 -0
- package/dist/observability-tool/errors.d.ts +8 -0
- package/dist/observability-tool/errors.d.ts.map +1 -0
- package/dist/observability-tool/index.d.ts +3 -0
- package/dist/observability-tool/index.d.ts.map +1 -0
- package/dist/observability-tool/logs.d.ts +22 -0
- package/dist/observability-tool/logs.d.ts.map +1 -0
- package/dist/observability-tool/metrics.d.ts +3 -0
- package/dist/observability-tool/metrics.d.ts.map +1 -0
- package/dist/observability-tool/shared.d.ts +18 -0
- package/dist/observability-tool/shared.d.ts.map +1 -0
- package/dist/observability-tool/trace.d.ts +3 -0
- package/dist/observability-tool/trace.d.ts.map +1 -0
- package/dist/observability-tool/types.d.ts +171 -0
- package/dist/observability-tool/types.d.ts.map +1 -0
- package/dist/session-tool/claude-code.d.ts +46 -0
- package/dist/session-tool/claude-code.d.ts.map +1 -0
- package/dist/session-tool/codex.d.ts +44 -0
- package/dist/session-tool/codex.d.ts.map +1 -0
- package/dist/session-tool/config.d.ts +29 -0
- package/dist/session-tool/config.d.ts.map +1 -0
- package/dist/session-tool/errors.d.ts +27 -0
- package/dist/session-tool/errors.d.ts.map +1 -0
- package/dist/session-tool/index.d.ts +13 -0
- package/dist/session-tool/index.d.ts.map +1 -0
- package/dist/session-tool/pi.d.ts +40 -0
- package/dist/session-tool/pi.d.ts.map +1 -0
- package/dist/session-tool/service.d.ts +18 -0
- package/dist/session-tool/service.d.ts.map +1 -0
- package/dist/session-tool/summaries.d.ts +5 -0
- package/dist/session-tool/summaries.d.ts.map +1 -0
- package/dist/session-tool/types.d.ts +38 -0
- package/dist/session-tool/types.d.ts.map +1 -0
- package/dist/shared/audit.d.ts +40 -0
- package/dist/shared/audit.d.ts.map +1 -0
- package/dist/shared/binary-preflight.d.ts +27 -0
- package/dist/shared/binary-preflight.d.ts.map +1 -0
- package/dist/shared/bun.d.ts +4 -0
- package/dist/shared/bun.d.ts.map +1 -0
- package/dist/shared/cli.d.ts +18 -0
- package/dist/shared/cli.d.ts.map +1 -0
- package/dist/shared/env-template.d.ts +9 -0
- package/dist/shared/env-template.d.ts.map +1 -0
- package/dist/shared/error-renderer.d.ts +4 -0
- package/dist/shared/error-renderer.d.ts.map +1 -0
- package/dist/shared/exec.d.ts +30 -0
- package/dist/shared/exec.d.ts.map +1 -0
- package/dist/shared/format.d.ts +7 -0
- package/dist/shared/format.d.ts.map +1 -0
- package/dist/shared/index.d.ts +12 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/k8s-probe.d.ts +11 -0
- package/dist/shared/k8s-probe.d.ts.map +1 -0
- package/dist/shared/log-transform.d.ts +2 -0
- package/dist/shared/log-transform.d.ts.map +1 -0
- package/dist/shared/path.d.ts +2 -0
- package/dist/shared/path.d.ts.map +1 -0
- package/dist/shared/prerequisites/config.d.ts +6 -0
- package/dist/shared/prerequisites/config.d.ts.map +1 -0
- package/dist/shared/prerequisites/driver-commands.d.ts +19 -0
- package/dist/shared/prerequisites/driver-commands.d.ts.map +1 -0
- package/dist/shared/prerequisites/errors.d.ts +10 -0
- package/dist/shared/prerequisites/errors.d.ts.map +1 -0
- package/dist/shared/prerequisites/guardian-entry.d.ts +2 -0
- package/dist/shared/prerequisites/guardian-entry.d.ts.map +1 -0
- package/dist/shared/prerequisites/guardian.d.ts +41 -0
- package/dist/shared/prerequisites/guardian.d.ts.map +1 -0
- package/dist/shared/prerequisites/index.d.ts +6 -0
- package/dist/shared/prerequisites/index.d.ts.map +1 -0
- package/dist/shared/prerequisites/runtime.d.ts +26 -0
- package/dist/shared/prerequisites/runtime.d.ts.map +1 -0
- package/dist/shared/prerequisites/store.d.ts +99 -0
- package/dist/shared/prerequisites/store.d.ts.map +1 -0
- package/dist/shared/prerequisites/types.d.ts +39 -0
- package/dist/shared/prerequisites/types.d.ts.map +1 -0
- package/dist/shared/prerequisites/vpn.d.ts +5 -0
- package/dist/shared/prerequisites/vpn.d.ts.map +1 -0
- package/dist/shared/schema-dump.d.ts +27 -0
- package/dist/shared/schema-dump.d.ts.map +1 -0
- package/dist/shared/throttle.d.ts +17 -0
- package/dist/shared/throttle.d.ts.map +1 -0
- package/dist/shared/transform.d.ts +49 -0
- package/dist/shared/transform.d.ts.map +1 -0
- package/dist/shared/types.d.ts +30 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/package.json +77 -20
- package/src/az-tool/service.ts +30 -23
- package/src/k8s-tool/context.ts +73 -0
- package/src/k8s-tool/service.ts +34 -62
- package/src/logs-tool/service.ts +78 -87
- package/src/shared/bun.ts +0 -7
- package/src/shared/exec.ts +58 -4
- package/src/shared/index.ts +2 -2
package/src/logs-tool/service.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, join, posix, relative, resolve } from "node:path";
|
|
2
3
|
|
|
3
|
-
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
4
4
|
import { Context, Effect, Layer, Result } from "effect";
|
|
5
5
|
|
|
6
6
|
import type { Environment, LogFile, ReadOptions } from "./types";
|
|
@@ -9,7 +9,6 @@ import { K8sService, K8sServiceLayer } from "#k8s/service";
|
|
|
9
9
|
import { ConfigService, ConfigServiceLayer, getToolConfig } from "#config/loader";
|
|
10
10
|
import type { LogsConfig } from "#config/types";
|
|
11
11
|
import { LogsNotFoundError, LogsReadError, type LogsError } from "./errors";
|
|
12
|
-
import { collectProcessOutput } from "#shared/exec";
|
|
13
12
|
import { transformLogOutput } from "./transformers";
|
|
14
13
|
|
|
15
14
|
export const parseLogFiles = (output: string): LogFile[] => {
|
|
@@ -41,11 +40,36 @@ export const formatPrettyOutput = (output: string): string => {
|
|
|
41
40
|
.join("\n---\n");
|
|
42
41
|
};
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
const errorText = (error: unknown): string =>
|
|
44
|
+
error instanceof Error ? error.message : String(error);
|
|
45
|
+
|
|
46
|
+
const takeLastLines = (contents: string, lines: number): string => {
|
|
47
|
+
const all = contents.split("\n");
|
|
48
|
+
const withoutTrailingBlank = all.at(-1) === "" ? all.slice(0, -1) : all;
|
|
49
|
+
return withoutTrailingBlank.slice(-lines).join("\n");
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** Newest first, so callers can take the head as the latest log the way `ls -t` did. */
|
|
53
|
+
const readLocalLogDirectory = async (localDir: string): Promise<LogFile[]> => {
|
|
54
|
+
const entries = await readdir(localDir, { withFileTypes: true });
|
|
55
|
+
const logs = entries.filter((entry) => !entry.isDirectory() && entry.name.endsWith(".log"));
|
|
56
|
+
|
|
57
|
+
const described = await Promise.all(
|
|
58
|
+
logs.map(async (entry) => {
|
|
59
|
+
const stats = await stat(join(localDir, entry.name));
|
|
60
|
+
return {
|
|
61
|
+
name: entry.name,
|
|
62
|
+
size: String(stats.size),
|
|
63
|
+
date: stats.mtime.toISOString(),
|
|
64
|
+
modifiedAtMs: stats.mtimeMs,
|
|
65
|
+
};
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return described
|
|
70
|
+
.toSorted((left, right) => right.modifiedAtMs - left.modifiedAtMs)
|
|
71
|
+
.map(({ name, size, date }) => ({ name, size, date }));
|
|
72
|
+
};
|
|
49
73
|
|
|
50
74
|
const readCommandOutput = (output: unknown): string => (typeof output === "string" ? output : "");
|
|
51
75
|
const filterLogLines = (output: string, grep: string | undefined): string => {
|
|
@@ -91,45 +115,8 @@ export class LogsService extends Context.Service<
|
|
|
91
115
|
LogsService,
|
|
92
116
|
Effect.gen(function* () {
|
|
93
117
|
const k8s = yield* K8sService;
|
|
94
|
-
const executor = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
95
118
|
const config = yield* ConfigService;
|
|
96
119
|
|
|
97
|
-
const runShellCommand = (commandStr: string) =>
|
|
98
|
-
Effect.scoped(
|
|
99
|
-
Effect.gen(function* () {
|
|
100
|
-
const command = ChildProcess.make("sh", ["-c", commandStr], {
|
|
101
|
-
stdout: "pipe",
|
|
102
|
-
stderr: "pipe",
|
|
103
|
-
});
|
|
104
|
-
const process = yield* executor.spawn(command);
|
|
105
|
-
return yield* collectProcessOutput(process);
|
|
106
|
-
}),
|
|
107
|
-
).pipe(
|
|
108
|
-
Effect.catch((platformError) =>
|
|
109
|
-
Effect.succeed({
|
|
110
|
-
stdout: "",
|
|
111
|
-
stderr: String(platformError),
|
|
112
|
-
exitCode: -1,
|
|
113
|
-
}),
|
|
114
|
-
),
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
const runDirectCommand = (executable: string, args: readonly string[]) =>
|
|
118
|
-
Effect.scoped(
|
|
119
|
-
Effect.gen(function* () {
|
|
120
|
-
const command = ChildProcess.make(executable, args, {
|
|
121
|
-
stdout: "pipe",
|
|
122
|
-
stderr: "pipe",
|
|
123
|
-
});
|
|
124
|
-
const process = yield* executor.spawn(command);
|
|
125
|
-
return yield* collectProcessOutput(process);
|
|
126
|
-
}),
|
|
127
|
-
).pipe(
|
|
128
|
-
Effect.catch((platformError) =>
|
|
129
|
-
Effect.succeed({ stdout: "", stderr: String(platformError), exitCode: -1 }),
|
|
130
|
-
),
|
|
131
|
-
);
|
|
132
|
-
|
|
133
120
|
const getLogsConfig = (profile?: string): LogsConfig | undefined =>
|
|
134
121
|
getToolConfig<LogsConfig>(config, "logs", profile);
|
|
135
122
|
|
|
@@ -137,16 +124,16 @@ export class LogsService extends Context.Service<
|
|
|
137
124
|
logsConfig: LogsConfig,
|
|
138
125
|
) {
|
|
139
126
|
const localDir = logsConfig.localDir;
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
127
|
+
const files = yield* Effect.tryPromise(() => readLocalLogDirectory(localDir)).pipe(
|
|
128
|
+
Effect.mapError(
|
|
129
|
+
(error) =>
|
|
130
|
+
new LogsReadError({
|
|
131
|
+
message: errorText(error) || "Failed to list local logs",
|
|
132
|
+
source: localDir,
|
|
133
|
+
}),
|
|
134
|
+
),
|
|
135
|
+
);
|
|
148
136
|
|
|
149
|
-
const files = parseLogFiles(result.stdout);
|
|
150
137
|
if (files.length === 0) {
|
|
151
138
|
return yield* new LogsNotFoundError({
|
|
152
139
|
message: "No log files found",
|
|
@@ -213,24 +200,25 @@ export class LogsService extends Context.Service<
|
|
|
213
200
|
let logFile = options.file;
|
|
214
201
|
|
|
215
202
|
if (!logFile) {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
203
|
+
const files = yield* Effect.tryPromise(() => readLocalLogDirectory(localDir)).pipe(
|
|
204
|
+
Effect.mapError(
|
|
205
|
+
(error) =>
|
|
206
|
+
new LogsReadError({
|
|
207
|
+
message: errorText(error) || "Failed to find latest log",
|
|
208
|
+
source: localDir,
|
|
209
|
+
}),
|
|
210
|
+
),
|
|
211
|
+
);
|
|
224
212
|
|
|
225
|
-
const
|
|
226
|
-
if (!
|
|
213
|
+
const latest = files[0];
|
|
214
|
+
if (!latest) {
|
|
227
215
|
return yield* new LogsNotFoundError({
|
|
228
216
|
message: "No log files found",
|
|
229
217
|
path: localDir,
|
|
230
218
|
});
|
|
231
219
|
}
|
|
232
220
|
|
|
233
|
-
logFile =
|
|
221
|
+
logFile = latest.name;
|
|
234
222
|
}
|
|
235
223
|
|
|
236
224
|
const lexicalPath = resolveLocalLogPath(localDir, logFile);
|
|
@@ -240,20 +228,22 @@ export class LogsService extends Context.Service<
|
|
|
240
228
|
source: localDir,
|
|
241
229
|
});
|
|
242
230
|
}
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
231
|
+
const canonical = yield* Effect.tryPromise(async () => ({
|
|
232
|
+
base: await realpath(localDir),
|
|
233
|
+
path: await realpath(lexicalPath),
|
|
234
|
+
})).pipe(
|
|
235
|
+
Effect.mapError(
|
|
236
|
+
(error) =>
|
|
237
|
+
new LogsReadError({
|
|
238
|
+
message: errorText(error) || "Failed to canonicalize the log path",
|
|
239
|
+
source: lexicalPath,
|
|
240
|
+
}),
|
|
241
|
+
),
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
const canonicalPath = canonical.path;
|
|
253
245
|
if (
|
|
254
|
-
|
|
255
|
-
canonicalPath === undefined ||
|
|
256
|
-
resolveLocalLogPath(canonicalBase, canonicalPath) !== canonicalPath ||
|
|
246
|
+
resolveLocalLogPath(canonical.base, canonicalPath) !== canonicalPath ||
|
|
257
247
|
!canonicalPath.endsWith(".log")
|
|
258
248
|
) {
|
|
259
249
|
return yield* new LogsReadError({
|
|
@@ -261,17 +251,18 @@ export class LogsService extends Context.Service<
|
|
|
261
251
|
source: localDir,
|
|
262
252
|
});
|
|
263
253
|
}
|
|
264
|
-
const command = `tail -${options.tail} ${sanitizeShellArg(canonicalPath)}`;
|
|
265
|
-
const result = yield* runShellCommand(command);
|
|
266
254
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
255
|
+
const contents = yield* Effect.tryPromise(() => readFile(canonicalPath, "utf8")).pipe(
|
|
256
|
+
Effect.mapError(
|
|
257
|
+
(error) =>
|
|
258
|
+
new LogsReadError({
|
|
259
|
+
message: errorText(error) || "Failed to read the log file",
|
|
260
|
+
source: canonicalPath,
|
|
261
|
+
}),
|
|
262
|
+
),
|
|
263
|
+
);
|
|
273
264
|
|
|
274
|
-
const output = filterLogLines(
|
|
265
|
+
const output = filterLogLines(takeLastLines(contents, options.tail), options.grep).trim();
|
|
275
266
|
if (!output) {
|
|
276
267
|
return "(no matching lines)";
|
|
277
268
|
}
|
package/src/shared/bun.ts
CHANGED
|
@@ -50,10 +50,3 @@ export async function runCommand(
|
|
|
50
50
|
executionTimeMs: Date.now() - startTime,
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
-
|
|
54
|
-
export function runShellCommand(
|
|
55
|
-
command: string,
|
|
56
|
-
options: CommandOptions = {},
|
|
57
|
-
): Promise<CommandResult> {
|
|
58
|
-
return runCommand(["sh", "-c", command], options);
|
|
59
|
-
}
|
package/src/shared/exec.ts
CHANGED
|
@@ -23,18 +23,21 @@ export const collectProcessOutput = (process: ChildProcessSpawner.ChildProcessHa
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
export const execEffect = (
|
|
26
|
-
|
|
26
|
+
executable: string,
|
|
27
|
+
args: readonly string[] = [],
|
|
27
28
|
timeoutMs: number = DEFAULT_TIMEOUT_MS,
|
|
28
29
|
): Effect.Effect<
|
|
29
30
|
{ stdout: string; stderr: string; exitCode: number },
|
|
30
31
|
ExecError,
|
|
31
32
|
ChildProcessSpawner.ChildProcessSpawner
|
|
32
|
-
> =>
|
|
33
|
-
|
|
33
|
+
> => {
|
|
34
|
+
const commandStr = renderCommandLine([executable, ...args]);
|
|
35
|
+
|
|
36
|
+
return Effect.scoped(
|
|
34
37
|
Effect.gen(function* () {
|
|
35
38
|
const executor = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
36
39
|
|
|
37
|
-
const command = ChildProcess.make(
|
|
40
|
+
const command = ChildProcess.make(executable, args, {
|
|
38
41
|
stdout: "pipe",
|
|
39
42
|
stderr: "pipe",
|
|
40
43
|
});
|
|
@@ -67,5 +70,56 @@ export const execEffect = (
|
|
|
67
70
|
),
|
|
68
71
|
),
|
|
69
72
|
);
|
|
73
|
+
};
|
|
70
74
|
|
|
71
75
|
export const quoteShellArg = (value: string) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Split a command line the way a POSIX shell would word-split it, without running one.
|
|
79
|
+
* Quotes group a value into a single argument and are removed; nothing else is interpreted,
|
|
80
|
+
* so a metacharacter reaches the child as literal text instead of shell syntax.
|
|
81
|
+
*/
|
|
82
|
+
export const tokenizeCommandLine = (commandLine: string): string[] => {
|
|
83
|
+
const tokens: string[] = [];
|
|
84
|
+
let current = "";
|
|
85
|
+
let started = false;
|
|
86
|
+
let quote: '"' | "'" | undefined;
|
|
87
|
+
|
|
88
|
+
for (const character of commandLine) {
|
|
89
|
+
if (quote) {
|
|
90
|
+
if (character === quote) {
|
|
91
|
+
quote = undefined;
|
|
92
|
+
} else {
|
|
93
|
+
current += character;
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (character === '"' || character === "'") {
|
|
99
|
+
quote = character;
|
|
100
|
+
started = true;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (/\s/.test(character)) {
|
|
105
|
+
if (started) {
|
|
106
|
+
tokens.push(current);
|
|
107
|
+
current = "";
|
|
108
|
+
started = false;
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
current += character;
|
|
114
|
+
started = true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (started) {
|
|
118
|
+
tokens.push(current);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return tokens;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export const renderCommandLine = (argv: readonly string[]): string =>
|
|
125
|
+
argv.map((arg) => (/^[A-Za-z0-9_./:=,@+-]+$/.test(arg) ? arg : quoteShellArg(arg))).join(" ");
|
package/src/shared/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type {
|
|
|
9
9
|
|
|
10
10
|
export { formatAny, formatOption, formatOutput, logFormatted } from "./format";
|
|
11
11
|
|
|
12
|
-
export { expandPath, runCommand
|
|
12
|
+
export { expandPath, runCommand } from "./bun";
|
|
13
13
|
|
|
14
14
|
export { commonArgOptions, parseCommonArgs } from "./cli";
|
|
15
15
|
|
|
@@ -21,7 +21,7 @@ export { dumpCommandSchema, makeSchemaCommand, type CommandSchema } from "./sche
|
|
|
21
21
|
import pkg from "../../package.json" with { type: "json" };
|
|
22
22
|
export const VERSION = pkg.version;
|
|
23
23
|
|
|
24
|
-
export { execEffect, type ExecError } from "./exec";
|
|
24
|
+
export { execEffect, renderCommandLine, tokenizeCommandLine, type ExecError } from "./exec";
|
|
25
25
|
|
|
26
26
|
export { createThrottle, type ThrottleError } from "./throttle";
|
|
27
27
|
|