birdclaw 0.5.1 → 0.7.0
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/CHANGELOG.md +92 -1
- package/README.md +75 -5
- package/package.json +8 -2
- package/scripts/browser-perf.mjs +1 -0
- package/scripts/start-test-server.mjs +16 -3
- package/src/cli.ts +812 -37
- package/src/components/AccountSwitcher.tsx +186 -0
- package/src/components/AppNav.tsx +37 -7
- package/src/components/AvatarChip.tsx +9 -3
- package/src/components/DmWorkspace.tsx +18 -8
- package/src/components/LinkPreviewCard.tsx +40 -18
- package/src/components/MarkdownViewer.tsx +818 -0
- package/src/components/ProfileAnalysisStream.tsx +428 -0
- package/src/components/ProfilePreview.tsx +120 -9
- package/src/components/SavedTimelineView.tsx +30 -8
- package/src/components/SyncNowButton.tsx +60 -25
- package/src/components/ThemeSlider.tsx +55 -50
- package/src/components/TimelineCard.tsx +240 -92
- package/src/components/TimelineRouteFrame.tsx +38 -8
- package/src/components/TweetMediaGrid.tsx +87 -38
- package/src/components/TweetRichText.tsx +45 -17
- package/src/components/account-selection.ts +64 -0
- package/src/components/useTimelineRouteData.ts +97 -13
- package/src/lib/account-sync-job.ts +666 -0
- package/src/lib/actions-transport.ts +216 -146
- package/src/lib/api-client.ts +128 -53
- package/src/lib/archive-finder.ts +78 -63
- package/src/lib/archive-import.ts +1593 -1291
- package/src/lib/authored-live.ts +262 -204
- package/src/lib/avatar-cache.ts +208 -43
- package/src/lib/backup.ts +1536 -954
- package/src/lib/bird-actions.ts +139 -57
- package/src/lib/bird-command.ts +101 -28
- package/src/lib/bird.ts +582 -194
- package/src/lib/blocklist.ts +40 -23
- package/src/lib/blocks-write.ts +129 -80
- package/src/lib/blocks.ts +165 -97
- package/src/lib/bookmark-sync-job.ts +250 -160
- package/src/lib/config.ts +35 -2
- package/src/lib/conversation-surface.ts +79 -48
- package/src/lib/data-sources.ts +219 -0
- package/src/lib/db.ts +95 -4
- package/src/lib/dms-live.ts +720 -66
- package/src/lib/effect-runtime.ts +45 -0
- package/src/lib/follow-graph.ts +224 -180
- package/src/lib/geocoding.ts +296 -0
- package/src/lib/http-effect.ts +222 -0
- package/src/lib/inbox.ts +74 -43
- package/src/lib/link-index.ts +88 -76
- package/src/lib/link-insights.ts +24 -0
- package/src/lib/link-preview-metadata.ts +472 -52
- package/src/lib/location.ts +137 -0
- package/src/lib/media-fetch.ts +286 -213
- package/src/lib/mention-threads-live.ts +445 -288
- package/src/lib/mentions-live.ts +549 -354
- package/src/lib/moderation-target.ts +102 -65
- package/src/lib/moderation-write.ts +77 -18
- package/src/lib/mutes-write.ts +129 -80
- package/src/lib/mutes.ts +8 -1
- package/src/lib/network-map.ts +382 -0
- package/src/lib/openai.ts +84 -53
- package/src/lib/period-digest.ts +1317 -0
- package/src/lib/profile-affiliation-hydration.ts +93 -54
- package/src/lib/profile-analysis.ts +1272 -0
- package/src/lib/profile-bio-entities.ts +1 -1
- package/src/lib/profile-hydration.ts +124 -72
- package/src/lib/profile-replies.ts +60 -43
- package/src/lib/profile-resolver.ts +402 -294
- package/src/lib/queries.ts +983 -203
- package/src/lib/research.ts +165 -120
- package/src/lib/search-discussion.ts +1016 -0
- package/src/lib/sqlite.ts +1 -0
- package/src/lib/timeline-collections-live.ts +204 -167
- package/src/lib/timeline-live.ts +325 -51
- package/src/lib/tweet-account-edges.ts +2 -0
- package/src/lib/tweet-lookup.ts +30 -19
- package/src/lib/tweet-render.ts +141 -1
- package/src/lib/tweet-search-live.ts +565 -0
- package/src/lib/types.ts +75 -3
- package/src/lib/ui.ts +31 -8
- package/src/lib/url-expansion.ts +226 -55
- package/src/lib/url-safety.ts +220 -0
- package/src/lib/web-sync.ts +222 -149
- package/src/lib/whois.ts +166 -147
- package/src/lib/x-web.ts +102 -71
- package/src/lib/xurl-rate-limits.ts +267 -0
- package/src/lib/xurl.ts +1185 -405
- package/src/routeTree.gen.ts +273 -0
- package/src/routes/__root.tsx +24 -5
- package/src/routes/api/action.tsx +127 -78
- package/src/routes/api/avatar.tsx +39 -30
- package/src/routes/api/blocks.tsx +26 -23
- package/src/routes/api/conversation.tsx +25 -14
- package/src/routes/api/data-sources.tsx +24 -0
- package/src/routes/api/inbox.tsx +27 -21
- package/src/routes/api/link-insights.tsx +31 -25
- package/src/routes/api/link-preview.tsx +25 -21
- package/src/routes/api/network-map.tsx +55 -0
- package/src/routes/api/period-digest.tsx +133 -0
- package/src/routes/api/profile-analysis.tsx +152 -0
- package/src/routes/api/profile-hydrate.tsx +31 -28
- package/src/routes/api/query.tsx +80 -55
- package/src/routes/api/search-discussion.tsx +169 -0
- package/src/routes/api/status.tsx +18 -10
- package/src/routes/api/sync.tsx +75 -29
- package/src/routes/api/xurl-rate-limits.tsx +24 -0
- package/src/routes/data-sources.tsx +255 -0
- package/src/routes/discuss.tsx +419 -0
- package/src/routes/dms.tsx +95 -28
- package/src/routes/inbox.tsx +32 -19
- package/src/routes/network-map.tsx +1035 -0
- package/src/routes/profile-analyze.tsx +112 -0
- package/src/routes/profiles.$handle.tsx +228 -0
- package/src/routes/rate-limits.tsx +309 -0
- package/src/routes/today.tsx +455 -0
- package/src/styles.css +22 -0
package/src/lib/bird-actions.ts
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import {
|
|
3
|
+
BirdCommandExecutionError,
|
|
4
|
+
runBirdCommandEffect,
|
|
5
|
+
} from "./bird-command";
|
|
6
|
+
import { runEffectPromise } from "./effect-runtime";
|
|
2
7
|
import type { XurlMentionUser } from "./types";
|
|
3
8
|
|
|
4
9
|
function liveWritesDisabled() {
|
|
5
10
|
return process.env.BIRDCLAW_DISABLE_LIVE_WRITES === "1";
|
|
6
11
|
}
|
|
7
12
|
|
|
13
|
+
function e2eFakeLiveWritesEnabled() {
|
|
14
|
+
return (
|
|
15
|
+
process.env.BIRDCLAW_E2E === "1" &&
|
|
16
|
+
process.env.BIRDCLAW_E2E_FAKE_LIVE_WRITES === "1"
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
8
20
|
function stripAnsi(value: string) {
|
|
9
21
|
// ANSI escape parsing needs a constructor to avoid literal control characters.
|
|
10
22
|
return value.replace(new RegExp("\\u001b\\[[0-9;]*m", "g"), "");
|
|
@@ -14,6 +26,9 @@ function formatExecError(error: unknown, fallback: string) {
|
|
|
14
26
|
if (!(error instanceof Error)) {
|
|
15
27
|
return fallback;
|
|
16
28
|
}
|
|
29
|
+
if (error instanceof BirdCommandExecutionError && error.useFallbackMessage) {
|
|
30
|
+
return fallback;
|
|
31
|
+
}
|
|
17
32
|
|
|
18
33
|
const parts = [error.message];
|
|
19
34
|
if (
|
|
@@ -38,18 +53,43 @@ function normalizeOutput(stdout?: string, stderr?: string) {
|
|
|
38
53
|
return stripAnsi(stdout || stderr || "ok").trim();
|
|
39
54
|
}
|
|
40
55
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
function toError(error: unknown) {
|
|
57
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function runBirdJsonCommandEffect(args: string[]) {
|
|
61
|
+
return Effect.gen(function* () {
|
|
62
|
+
const { stdout } = yield* runBirdCommandEffect(args);
|
|
63
|
+
return yield* Effect.try({
|
|
64
|
+
try: () => JSON.parse(stripAnsi(stdout)) as Record<string, unknown>,
|
|
65
|
+
catch: toError,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
44
68
|
}
|
|
45
69
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
70
|
+
function safeBirdProfileArg(query: string) {
|
|
71
|
+
const normalized = query.trim().replace(/^@/, "");
|
|
72
|
+
if (
|
|
73
|
+
/^\d{1,30}$/.test(normalized) ||
|
|
74
|
+
/^[A-Za-z0-9_]{1,15}$/.test(normalized)
|
|
75
|
+
) {
|
|
76
|
+
return normalized;
|
|
52
77
|
}
|
|
78
|
+
throw new Error("Invalid bird profile query");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function readBirdStatusViaBirdEffect(query: string) {
|
|
82
|
+
return Effect.gen(function* () {
|
|
83
|
+
const safeQuery = yield* Effect.try({
|
|
84
|
+
try: () => safeBirdProfileArg(query),
|
|
85
|
+
catch: toError,
|
|
86
|
+
});
|
|
87
|
+
return yield* runBirdJsonCommandEffect(["status", safeQuery, "--json"]);
|
|
88
|
+
}).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function readBirdStatusViaBird(query: string) {
|
|
92
|
+
return runEffectPromise(readBirdStatusViaBirdEffect(query));
|
|
53
93
|
}
|
|
54
94
|
|
|
55
95
|
function toBirdLookupUser(payload: Record<string, unknown>): XurlMentionUser {
|
|
@@ -112,22 +152,31 @@ function toBirdLookupUser(payload: Record<string, unknown>): XurlMentionUser {
|
|
|
112
152
|
};
|
|
113
153
|
}
|
|
114
154
|
|
|
115
|
-
export
|
|
116
|
-
|
|
117
|
-
const
|
|
155
|
+
export function lookupProfileViaBirdEffect(query: string) {
|
|
156
|
+
return Effect.gen(function* () {
|
|
157
|
+
const safeQuery = yield* Effect.try({
|
|
158
|
+
try: () => safeBirdProfileArg(query),
|
|
159
|
+
catch: toError,
|
|
160
|
+
});
|
|
161
|
+
const payload = yield* runBirdJsonCommandEffect([
|
|
118
162
|
"user",
|
|
119
|
-
|
|
163
|
+
safeQuery,
|
|
120
164
|
"-n",
|
|
121
165
|
"1",
|
|
122
166
|
"--json",
|
|
123
167
|
]);
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
168
|
+
return yield* Effect.try({
|
|
169
|
+
try: () => toBirdLookupUser(payload),
|
|
170
|
+
catch: toError,
|
|
171
|
+
});
|
|
172
|
+
}).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
128
173
|
}
|
|
129
174
|
|
|
130
|
-
|
|
175
|
+
export function lookupProfileViaBird(query: string) {
|
|
176
|
+
return runEffectPromise(lookupProfileViaBirdEffect(query));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function runVerifiedBirdMutationEffect({
|
|
131
180
|
action,
|
|
132
181
|
query,
|
|
133
182
|
verifyField,
|
|
@@ -138,45 +187,62 @@ async function runVerifiedBirdMutation({
|
|
|
138
187
|
verifyField: "blocking" | "muting";
|
|
139
188
|
expectedValue: boolean;
|
|
140
189
|
}) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
190
|
+
return Effect.gen(function* () {
|
|
191
|
+
const safeQuery = yield* Effect.try({
|
|
192
|
+
try: () => safeBirdProfileArg(query),
|
|
193
|
+
catch: toError,
|
|
194
|
+
});
|
|
195
|
+
if (liveWritesDisabled()) {
|
|
196
|
+
if (e2eFakeLiveWritesEnabled()) {
|
|
197
|
+
return { ok: true, output: "e2e fake live write" };
|
|
198
|
+
}
|
|
199
|
+
return { ok: false, output: "live writes disabled" };
|
|
200
|
+
}
|
|
144
201
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
202
|
+
const mutationResult = yield* runBirdCommandEffect([
|
|
203
|
+
action,
|
|
204
|
+
safeQuery,
|
|
205
|
+
]).pipe(
|
|
206
|
+
Effect.map(({ stdout, stderr }) => ({
|
|
207
|
+
ok: true as const,
|
|
208
|
+
output: normalizeOutput(stdout, stderr),
|
|
209
|
+
})),
|
|
210
|
+
Effect.catchAll((error) =>
|
|
211
|
+
Effect.succeed({
|
|
212
|
+
ok: false as const,
|
|
213
|
+
output: formatExecError(error, `bird ${action} failed`),
|
|
214
|
+
}),
|
|
215
|
+
),
|
|
216
|
+
);
|
|
217
|
+
if (!mutationResult.ok) {
|
|
218
|
+
return mutationResult;
|
|
219
|
+
}
|
|
155
220
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
221
|
+
const status = yield* readBirdStatusViaBirdEffect(query);
|
|
222
|
+
if (!status || typeof status[verifyField] !== "boolean") {
|
|
223
|
+
return {
|
|
224
|
+
ok: false,
|
|
225
|
+
output: `${mutationResult.output}; bird status verify unavailable`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const actualValue = Boolean(status[verifyField]);
|
|
230
|
+
if (actualValue !== expectedValue) {
|
|
231
|
+
return {
|
|
232
|
+
ok: false,
|
|
233
|
+
output: `${mutationResult.output}; bird status verify ${verifyField}=${String(actualValue)}`,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
163
236
|
|
|
164
|
-
const actualValue = Boolean(status[verifyField]);
|
|
165
|
-
if (actualValue !== expectedValue) {
|
|
166
237
|
return {
|
|
167
|
-
ok:
|
|
168
|
-
output: `${
|
|
238
|
+
ok: true,
|
|
239
|
+
output: `${mutationResult.output}; verified ${verifyField}=${String(actualValue)}`,
|
|
169
240
|
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return {
|
|
173
|
-
ok: true,
|
|
174
|
-
output: `${baseOutput}; verified ${verifyField}=${String(actualValue)}`,
|
|
175
|
-
};
|
|
241
|
+
});
|
|
176
242
|
}
|
|
177
243
|
|
|
178
|
-
export
|
|
179
|
-
return
|
|
244
|
+
export function blockUserViaBirdEffect(query: string) {
|
|
245
|
+
return runVerifiedBirdMutationEffect({
|
|
180
246
|
action: "block",
|
|
181
247
|
query,
|
|
182
248
|
verifyField: "blocking",
|
|
@@ -184,8 +250,12 @@ export async function blockUserViaBird(query: string) {
|
|
|
184
250
|
});
|
|
185
251
|
}
|
|
186
252
|
|
|
187
|
-
export
|
|
188
|
-
return
|
|
253
|
+
export function blockUserViaBird(query: string) {
|
|
254
|
+
return runEffectPromise(blockUserViaBirdEffect(query));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function unblockUserViaBirdEffect(query: string) {
|
|
258
|
+
return runVerifiedBirdMutationEffect({
|
|
189
259
|
action: "unblock",
|
|
190
260
|
query,
|
|
191
261
|
verifyField: "blocking",
|
|
@@ -193,8 +263,12 @@ export async function unblockUserViaBird(query: string) {
|
|
|
193
263
|
});
|
|
194
264
|
}
|
|
195
265
|
|
|
196
|
-
export
|
|
197
|
-
return
|
|
266
|
+
export function unblockUserViaBird(query: string) {
|
|
267
|
+
return runEffectPromise(unblockUserViaBirdEffect(query));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function muteUserViaBirdEffect(query: string) {
|
|
271
|
+
return runVerifiedBirdMutationEffect({
|
|
198
272
|
action: "mute",
|
|
199
273
|
query,
|
|
200
274
|
verifyField: "muting",
|
|
@@ -202,11 +276,19 @@ export async function muteUserViaBird(query: string) {
|
|
|
202
276
|
});
|
|
203
277
|
}
|
|
204
278
|
|
|
205
|
-
export
|
|
206
|
-
return
|
|
279
|
+
export function muteUserViaBird(query: string) {
|
|
280
|
+
return runEffectPromise(muteUserViaBirdEffect(query));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function unmuteUserViaBirdEffect(query: string) {
|
|
284
|
+
return runVerifiedBirdMutationEffect({
|
|
207
285
|
action: "unmute",
|
|
208
286
|
query,
|
|
209
287
|
verifyField: "muting",
|
|
210
288
|
expectedValue: false,
|
|
211
289
|
});
|
|
212
290
|
}
|
|
291
|
+
|
|
292
|
+
export function unmuteUserViaBird(query: string) {
|
|
293
|
+
return runEffectPromise(unmuteUserViaBirdEffect(query));
|
|
294
|
+
}
|
package/src/lib/bird-command.ts
CHANGED
|
@@ -3,10 +3,30 @@ import type { ExecFileOptions } from "node:child_process";
|
|
|
3
3
|
import { access } from "node:fs/promises";
|
|
4
4
|
import { constants } from "node:fs";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
|
+
import { Data, Effect } from "effect";
|
|
6
7
|
import { getBirdCommand } from "./config";
|
|
8
|
+
import { runEffectPromise } from "./effect-runtime";
|
|
7
9
|
|
|
8
10
|
const execFileAsync = promisify(execFile);
|
|
9
11
|
|
|
12
|
+
export class BirdCommandUnavailableError extends Data.TaggedError(
|
|
13
|
+
"BirdCommandUnavailableError",
|
|
14
|
+
)<{
|
|
15
|
+
readonly message: string;
|
|
16
|
+
readonly command: string;
|
|
17
|
+
readonly cause?: unknown;
|
|
18
|
+
}> {}
|
|
19
|
+
|
|
20
|
+
export class BirdCommandExecutionError extends Data.TaggedError(
|
|
21
|
+
"BirdCommandExecutionError",
|
|
22
|
+
)<{
|
|
23
|
+
readonly message: string;
|
|
24
|
+
readonly stdout?: string;
|
|
25
|
+
readonly stderr?: string;
|
|
26
|
+
readonly useFallbackMessage?: boolean;
|
|
27
|
+
readonly cause?: unknown;
|
|
28
|
+
}> {}
|
|
29
|
+
|
|
10
30
|
function isPathCommand(command: string) {
|
|
11
31
|
return command.includes("/") || command.startsWith(".");
|
|
12
32
|
}
|
|
@@ -18,40 +38,93 @@ function formatBirdInstallHint(command: string) {
|
|
|
18
38
|
].join("\n");
|
|
19
39
|
}
|
|
20
40
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
function isUnavailableExecError(error: unknown) {
|
|
42
|
+
return (
|
|
43
|
+
error &&
|
|
44
|
+
typeof error === "object" &&
|
|
45
|
+
"code" in error &&
|
|
46
|
+
(error.code === "ENOENT" || error.code === "EACCES")
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function execFailureFromCause(command: string, cause: unknown) {
|
|
51
|
+
if (isUnavailableExecError(cause)) {
|
|
52
|
+
return new BirdCommandUnavailableError({
|
|
53
|
+
message: formatBirdInstallHint(command),
|
|
54
|
+
command,
|
|
55
|
+
cause,
|
|
56
|
+
});
|
|
24
57
|
}
|
|
58
|
+
if (cause instanceof Error) {
|
|
59
|
+
const output = cause as Error & {
|
|
60
|
+
stdout?: unknown;
|
|
61
|
+
stderr?: unknown;
|
|
62
|
+
};
|
|
63
|
+
return new BirdCommandExecutionError({
|
|
64
|
+
message: cause.message,
|
|
65
|
+
stdout: typeof output.stdout === "string" ? output.stdout : undefined,
|
|
66
|
+
stderr: typeof output.stderr === "string" ? output.stderr : undefined,
|
|
67
|
+
cause,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return new BirdCommandExecutionError({
|
|
71
|
+
message: "",
|
|
72
|
+
useFallbackMessage: true,
|
|
73
|
+
cause,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
25
76
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
throw new Error(formatBirdInstallHint(command));
|
|
77
|
+
function assertBirdCommandAvailableEffect(command: string) {
|
|
78
|
+
if (!isPathCommand(command)) {
|
|
79
|
+
return Effect.void;
|
|
30
80
|
}
|
|
81
|
+
|
|
82
|
+
return Effect.tryPromise({
|
|
83
|
+
try: () => Promise.resolve(access(command, constants.X_OK)),
|
|
84
|
+
catch: (cause) =>
|
|
85
|
+
new BirdCommandUnavailableError({
|
|
86
|
+
message: formatBirdInstallHint(command),
|
|
87
|
+
command,
|
|
88
|
+
cause,
|
|
89
|
+
}),
|
|
90
|
+
}).pipe(Effect.asVoid);
|
|
31
91
|
}
|
|
32
92
|
|
|
33
|
-
|
|
93
|
+
function getBirdCommandEffect() {
|
|
94
|
+
return Effect.try({
|
|
95
|
+
try: () => getBirdCommand(),
|
|
96
|
+
catch: (cause) =>
|
|
97
|
+
cause instanceof Error ? cause : new Error(String(cause)),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function runBirdCommandEffect(
|
|
34
102
|
args: string[],
|
|
35
103
|
options?: ExecFileOptions,
|
|
36
|
-
):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
104
|
+
): Effect.Effect<
|
|
105
|
+
{ stdout: string; stderr: string },
|
|
106
|
+
BirdCommandExecutionError | BirdCommandUnavailableError | Error
|
|
107
|
+
> {
|
|
108
|
+
return Effect.gen(function* () {
|
|
109
|
+
const birdCommand = yield* getBirdCommandEffect();
|
|
110
|
+
yield* assertBirdCommandAvailableEffect(birdCommand);
|
|
111
|
+
|
|
112
|
+
const result = yield* Effect.tryPromise({
|
|
113
|
+
try: () =>
|
|
114
|
+
Promise.resolve(
|
|
115
|
+
options === undefined
|
|
116
|
+
? execFileAsync(birdCommand, args)
|
|
117
|
+
: execFileAsync(birdCommand, args, options),
|
|
118
|
+
),
|
|
119
|
+
catch: (cause) => execFailureFromCause(birdCommand, cause),
|
|
120
|
+
});
|
|
45
121
|
return result as { stdout: string; stderr: string };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function runBirdCommand(
|
|
126
|
+
args: string[],
|
|
127
|
+
options?: ExecFileOptions,
|
|
128
|
+
): Promise<{ stdout: string; stderr: string }> {
|
|
129
|
+
return runEffectPromise(runBirdCommandEffect(args, options));
|
|
57
130
|
}
|