@mepuka/skygent 0.2.0 → 0.3.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/README.md +269 -31
- package/index.ts +18 -3
- package/package.json +1 -1
- package/src/cli/app.ts +4 -2
- package/src/cli/compact-output.ts +52 -0
- package/src/cli/config.ts +46 -4
- package/src/cli/doc/table-renderers.ts +29 -0
- package/src/cli/doc/thread.ts +2 -4
- package/src/cli/exit-codes.ts +2 -0
- package/src/cli/feed.ts +78 -61
- package/src/cli/filter-dsl.ts +146 -11
- package/src/cli/filter-errors.ts +13 -11
- package/src/cli/filter-help.ts +7 -0
- package/src/cli/filter-input.ts +3 -2
- package/src/cli/filter.ts +83 -5
- package/src/cli/graph.ts +297 -169
- package/src/cli/input.ts +45 -0
- package/src/cli/interval.ts +4 -33
- package/src/cli/jetstream.ts +2 -0
- package/src/cli/layers.ts +10 -0
- package/src/cli/logging.ts +8 -0
- package/src/cli/option-schemas.ts +22 -0
- package/src/cli/output-format.ts +11 -0
- package/src/cli/output-render.ts +14 -0
- package/src/cli/pagination.ts +17 -0
- package/src/cli/parse-errors.ts +30 -0
- package/src/cli/parse.ts +1 -47
- package/src/cli/pipe-input.ts +18 -0
- package/src/cli/pipe.ts +154 -0
- package/src/cli/post.ts +88 -66
- package/src/cli/query-fields.ts +13 -3
- package/src/cli/query.ts +354 -100
- package/src/cli/search.ts +93 -136
- package/src/cli/shared-options.ts +11 -63
- package/src/cli/shared.ts +1 -20
- package/src/cli/store-errors.ts +28 -21
- package/src/cli/store-tree.ts +6 -4
- package/src/cli/store.ts +41 -2
- package/src/cli/stream-merge.ts +105 -0
- package/src/cli/sync-factory.ts +24 -7
- package/src/cli/sync.ts +46 -67
- package/src/cli/thread-options.ts +25 -0
- package/src/cli/time.ts +171 -0
- package/src/cli/view-thread.ts +29 -32
- package/src/cli/watch.ts +55 -26
- package/src/domain/errors.ts +6 -1
- package/src/domain/format.ts +21 -0
- package/src/domain/order.ts +24 -0
- package/src/domain/primitives.ts +20 -3
- package/src/graph/relationships.ts +129 -0
- package/src/services/bsky-client.ts +11 -5
- package/src/services/jetstream-sync.ts +4 -4
- package/src/services/lineage-store.ts +15 -1
- package/src/services/shared.ts +48 -1
- package/src/services/store-cleaner.ts +5 -2
- package/src/services/store-commit.ts +60 -0
- package/src/services/store-manager.ts +69 -2
- package/src/services/store-renamer.ts +288 -0
- package/src/services/store-stats.ts +7 -5
- package/src/services/sync-engine.ts +149 -89
- package/src/services/sync-reporter.ts +3 -1
- package/src/services/sync-settings.ts +24 -0
package/src/cli/filter.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Chunk, Clock, Effect, Option, Stream } from "effect";
|
|
|
4
4
|
import { StoreQuery } from "../domain/events.js";
|
|
5
5
|
import { RawPost } from "../domain/raw.js";
|
|
6
6
|
import type { Post } from "../domain/post.js";
|
|
7
|
-
import { StoreName } from "../domain/primitives.js";
|
|
7
|
+
import { PostUri, StoreName } from "../domain/primitives.js";
|
|
8
8
|
import { BskyClient } from "../services/bsky-client.js";
|
|
9
9
|
import { FilterCompiler } from "../services/filter-compiler.js";
|
|
10
10
|
import { FilterLibrary } from "../services/filter-library.js";
|
|
@@ -24,6 +24,8 @@ import { renderTableLegacy } from "./doc/table.js";
|
|
|
24
24
|
import { withExamples } from "./help.js";
|
|
25
25
|
import { filterOption, filterJsonOption } from "./shared-options.js";
|
|
26
26
|
import { jsonTableFormats, resolveOutputFormat, textJsonFormats } from "./output-format.js";
|
|
27
|
+
import { filterDslDescription, filterJsonDescription } from "./filter-help.js";
|
|
28
|
+
import { PositiveInt } from "./option-schemas.js";
|
|
27
29
|
|
|
28
30
|
const filterNameArg = Args.text({ name: "name" }).pipe(
|
|
29
31
|
Args.withSchema(StoreName),
|
|
@@ -35,6 +37,7 @@ const postJsonOption = Options.text("post-json").pipe(
|
|
|
35
37
|
Options.optional
|
|
36
38
|
);
|
|
37
39
|
const postUriOption = Options.text("post-uri").pipe(
|
|
40
|
+
Options.withSchema(PostUri),
|
|
38
41
|
Options.withDescription("Bluesky post URI (at://...)."),
|
|
39
42
|
Options.optional
|
|
40
43
|
);
|
|
@@ -42,7 +45,18 @@ const storeOption = Options.text("store").pipe(
|
|
|
42
45
|
Options.withSchema(StoreName),
|
|
43
46
|
Options.withDescription("Store name to sample for benchmarking")
|
|
44
47
|
);
|
|
48
|
+
const storeTestOption = Options.text("store").pipe(
|
|
49
|
+
Options.withSchema(StoreName),
|
|
50
|
+
Options.withDescription("Store name to sample for filter testing"),
|
|
51
|
+
Options.optional
|
|
52
|
+
);
|
|
53
|
+
const testLimitOption = Options.integer("limit").pipe(
|
|
54
|
+
Options.withSchema(PositiveInt),
|
|
55
|
+
Options.withDescription("Number of posts to evaluate (default: 100)"),
|
|
56
|
+
Options.optional
|
|
57
|
+
);
|
|
45
58
|
const sampleSizeOption = Options.integer("sample-size").pipe(
|
|
59
|
+
Options.withSchema(PositiveInt),
|
|
46
60
|
Options.withDescription("Number of posts to evaluate (default: 1000)"),
|
|
47
61
|
Options.optional
|
|
48
62
|
);
|
|
@@ -267,17 +281,68 @@ export const filterTest = Command.make(
|
|
|
267
281
|
filterJson: filterJsonOption,
|
|
268
282
|
postJson: postJsonOption,
|
|
269
283
|
postUri: postUriOption,
|
|
284
|
+
store: storeTestOption,
|
|
285
|
+
limit: testLimitOption,
|
|
270
286
|
format: testFormatOption
|
|
271
287
|
},
|
|
272
|
-
({ filter, filterJson, postJson, postUri, format }) =>
|
|
288
|
+
({ filter, filterJson, postJson, postUri, store, limit, format }) =>
|
|
273
289
|
Effect.gen(function* () {
|
|
274
290
|
yield* requireFilterExpr(filter, filterJson);
|
|
275
291
|
const expr = yield* parseFilterExpr(filter, filterJson);
|
|
276
292
|
const runtime = yield* FilterRuntime;
|
|
293
|
+
const outputFormat = Option.getOrElse(format, () => "text" as const);
|
|
294
|
+
if (Option.isSome(store)) {
|
|
295
|
+
if (Option.isSome(postJson) || Option.isSome(postUri)) {
|
|
296
|
+
return yield* CliInputError.make({
|
|
297
|
+
message: "Use either --store or --post-json/--post-uri, not both.",
|
|
298
|
+
cause: { store: store.value, postJson, postUri }
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
const storeRef = yield* storeOptions.loadStoreRef(store.value);
|
|
302
|
+
const index = yield* StoreIndex;
|
|
303
|
+
const evaluateBatch = yield* runtime.evaluateBatch(expr);
|
|
304
|
+
const sampleLimit = Option.getOrElse(limit, () => 100);
|
|
305
|
+
const query = StoreQuery.make({ scanLimit: sampleLimit, order: "desc" });
|
|
306
|
+
const stream = index.query(storeRef, query);
|
|
307
|
+
const result = yield* stream.pipe(
|
|
308
|
+
Stream.grouped(50),
|
|
309
|
+
Stream.runFoldEffect(
|
|
310
|
+
{ processed: 0, matched: 0 },
|
|
311
|
+
(state, batch) =>
|
|
312
|
+
evaluateBatch(batch).pipe(
|
|
313
|
+
Effect.map((results) => {
|
|
314
|
+
const processed = state.processed + Chunk.size(batch);
|
|
315
|
+
const matched =
|
|
316
|
+
state.matched +
|
|
317
|
+
Chunk.toReadonlyArray(results).filter(Boolean).length;
|
|
318
|
+
return { processed, matched };
|
|
319
|
+
})
|
|
320
|
+
)
|
|
321
|
+
)
|
|
322
|
+
);
|
|
323
|
+
if (outputFormat === "json") {
|
|
324
|
+
yield* writeJson({
|
|
325
|
+
store: storeRef.name,
|
|
326
|
+
processed: result.processed,
|
|
327
|
+
matched: result.matched,
|
|
328
|
+
limit: sampleLimit,
|
|
329
|
+
filter: expr,
|
|
330
|
+
filterText: formatFilterExpr(expr)
|
|
331
|
+
});
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const lines = [
|
|
335
|
+
`Matched: ${result.matched}/${result.processed}`,
|
|
336
|
+
`Store: ${storeRef.name}`,
|
|
337
|
+
`Filter: ${formatFilterExpr(expr)}`
|
|
338
|
+
];
|
|
339
|
+
yield* writeText(lines.join("\n"));
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
277
343
|
const predicate = yield* runtime.evaluate(expr);
|
|
278
344
|
const post = yield* loadPost(postJson, postUri);
|
|
279
345
|
const ok = yield* predicate(post);
|
|
280
|
-
const outputFormat = Option.getOrElse(format, () => "text" as const);
|
|
281
346
|
if (outputFormat === "json") {
|
|
282
347
|
yield* writeJson({
|
|
283
348
|
ok,
|
|
@@ -297,8 +362,9 @@ export const filterTest = Command.make(
|
|
|
297
362
|
})
|
|
298
363
|
).pipe(
|
|
299
364
|
Command.withDescription(
|
|
300
|
-
withExamples("Test a filter against a
|
|
301
|
-
"skygent filter test --filter 'hashtag:#ai' --post-uri at://did:plc:example/app.bsky.feed.post/xyz"
|
|
365
|
+
withExamples("Test a filter against a post or store sample", [
|
|
366
|
+
"skygent filter test --filter 'hashtag:#ai' --post-uri at://did:plc:example/app.bsky.feed.post/xyz",
|
|
367
|
+
"skygent filter test --filter 'engagement:minLikes=10' --store my-store --limit 100"
|
|
302
368
|
])
|
|
303
369
|
)
|
|
304
370
|
);
|
|
@@ -414,8 +480,20 @@ export const filterDescribe = Command.make(
|
|
|
414
480
|
)
|
|
415
481
|
);
|
|
416
482
|
|
|
483
|
+
export const filterHelp = Command.make("help", {}, () =>
|
|
484
|
+
Effect.gen(function* () {
|
|
485
|
+
const body = [filterDslDescription(), "", filterJsonDescription()].join("\n");
|
|
486
|
+
yield* writeText(body);
|
|
487
|
+
})
|
|
488
|
+
).pipe(
|
|
489
|
+
Command.withDescription(
|
|
490
|
+
withExamples("Show filter DSL and JSON help", ["skygent filter help"])
|
|
491
|
+
)
|
|
492
|
+
);
|
|
493
|
+
|
|
417
494
|
export const filterCommand = Command.make("filter", {}).pipe(
|
|
418
495
|
Command.withSubcommands([
|
|
496
|
+
filterHelp,
|
|
419
497
|
filterList,
|
|
420
498
|
filterShow,
|
|
421
499
|
filterCreate,
|