Showing the changes to packages/platform (2 of 4 files). Show all
Unified · Split
| @@ -0,0 +1,127 @@ | |||
| 1 | import { | ||
| 2 | describe, | ||
| 3 | expect, | ||
| 4 | it, | ||
| 5 | } from "@effect/vitest"; | ||
| 6 | import * as Effect from "effect/Effect"; | ||
| 7 | |||
| 8 | import { | ||
| 9 | createScopeSourceCatalog, | ||
| 10 | } from "./source-catalog"; | ||
| 11 | |||
| 12 | const makeLoadedCatalog = (input: { | ||
| 13 | sourceId: string; | ||
| 14 | toolPaths: ReadonlyArray<string>; | ||
| 15 | enabled?: boolean; | ||
| 16 | status?: "connected" | "error"; | ||
| 17 | }) => ({ | ||
| 18 | source: { | ||
| 19 | id: input.sourceId, | ||
| 20 | scopeId: "ws_test", | ||
| 21 | name: input.sourceId, | ||
| 22 | kind: "openapi", | ||
| 23 | endpoint: `https://example.test/${input.sourceId}`, | ||
| 24 | status: input.status ?? "connected", | ||
| 25 | enabled: input.enabled ?? true, | ||
| 26 | namespace: null, | ||
| 27 | bindingVersion: 1, | ||
| 28 | binding: {}, | ||
| 29 | importAuthPolicy: "reuse_runtime", | ||
| 30 | importAuth: { | ||
| 31 | kind: "none", | ||
| 32 | }, | ||
| 33 | auth: { | ||
| 34 | kind: "none", | ||
| 35 | }, | ||
| 36 | sourceHash: null, | ||
| 37 | lastError: null, | ||
| 38 | createdAt: 0, | ||
| 39 | updatedAt: 0, | ||
| 40 | }, | ||
| 41 | projected: { | ||
| 42 | toolDescriptors: Object.fromEntries( | ||
| 43 | input.toolPaths.map((path, index) => [ | ||
| 44 | `cap_${index}`, | ||
| 45 | { | ||
| 46 | toolPath: path.split("."), | ||
| 47 | }, | ||
| 48 | ]), | ||
| 49 | ), | ||
| 50 | }, | ||
| 51 | }) as any; | ||
| 52 | |||
| 53 | const noopScopeConfigStore = { | ||
| 54 | load: () => Effect.die("unexpected config load"), | ||
| 55 | writeProject: () => Effect.void, | ||
| 56 | resolveRelativePath: (path: string) => Effect.succeed(path), | ||
| 57 | } as any; | ||
| 58 | |||
| 59 | const noopScopeStateStore = { | ||
| 60 | load: () => Effect.die("unexpected scope state load"), | ||
| 61 | write: () => Effect.void, | ||
| 62 | } as any; | ||
| 63 | |||
| 64 | const noopSourceArtifactStore = { | ||
| 65 | build: () => Effect.die("unexpected source artifact build"), | ||
| 66 | read: () => Effect.die("unexpected source artifact read"), | ||
| 67 | write: () => Effect.void, | ||
| 68 | remove: () => Effect.void, | ||
| 69 | } as any; | ||
| 70 | |||
| 71 | describe("scope source catalog", () => { | ||
| 72 | it.effect("lists namespaces from projected tool paths without loading the tool index", () => | ||
| 73 | Effect.gen(function* () { | ||
| 74 | const catalog = createScopeSourceCatalog({ | ||
| 75 | scopeId: "ws_test" as any, | ||
| 76 | actorScopeId: "acc_test" as any, | ||
| 77 | sourceCatalogStore: { | ||
| 78 | loadWorkspaceSourceCatalogs: () => Effect.succeed([ | ||
| 79 | makeLoadedCatalog({ | ||
| 80 | sourceId: "github", | ||
| 81 | toolPaths: [ | ||
| 82 | "github.repos.get", | ||
| 83 | "github.repos.list", | ||
| 84 | "github.issues.get", | ||
| 85 | ], | ||
| 86 | }), | ||
| 87 | makeLoadedCatalog({ | ||
| 88 | sourceId: "linear", | ||
| 89 | toolPaths: [ | ||
| 90 | "linear.issue.create", | ||
| 91 | ], | ||
| 92 | }), | ||
| 93 | makeLoadedCatalog({ | ||
| 94 | sourceId: "disabled", | ||
| 95 | toolPaths: [ | ||
| 96 | "disabled.admin.delete", | ||
| 97 | ], | ||
| 98 | enabled: false, | ||
| 99 | }), | ||
| 100 | ]), | ||
| 101 | loadWorkspaceSourceCatalogToolIndex: () => Effect.die("unexpected tool index load"), | ||
| 102 | loadWorkspaceSourceCatalogToolByPath: () => Effect.die("unexpected tool lookup"), | ||
| 103 | } as any, | ||
| 104 | scopeConfigStore: noopScopeConfigStore, | ||
| 105 | scopeStateStore: noopScopeStateStore, | ||
| 106 | sourceArtifactStore: noopSourceArtifactStore, | ||
| 107 | runtimeLocalScope: null, | ||
| 108 | }); | ||
| 109 | |||
| 110 | const namespaces = yield* catalog.listNamespaces({ limit: 10 }); | ||
| 111 | |||
| 112 | expect(namespaces).toEqual([ | ||
| 113 | { | ||
| 114 | namespace: "github.issues", | ||
| 115 | toolCount: 1, | ||
| 116 | }, | ||
| 117 | { | ||
| 118 | namespace: "github.repos", | ||
| 119 | toolCount: 2, | ||
| 120 | }, | ||
| 121 | { | ||
| 122 | namespace: "linear.issue", | ||
| 123 | toolCount: 1, | ||
| 124 | }, | ||
| 125 | ]); | ||
| 126 | })); | ||
| 127 | }); | ||
| @@ -1,6 +1,7 @@ | |||
| 1 | import { | 1 | import { |
| 2 | createToolCatalogFromEntries, | 2 | createToolCatalogFromEntries, |
| 3 | type ToolCatalog, | 3 | type ToolCatalog, |
| 4 | type ToolNamespace, | ||
| 4 | } from "@executor/codemode-core"; | 5 | } from "@executor/codemode-core"; |
| 5 | import type { | 6 | import type { |
| 6 | ScopeId, | 7 | ScopeId, |
| @@ -80,6 +81,49 @@ const hasSubstringMatch = (value: string, queryToken: string): boolean => { · 73 unmodified lines | |||
| 80 | const queryTokenWeight = (token: string): number => | 81 | const queryTokenWeight = (token: string): number => |
| 81 | LOW_SIGNAL_QUERY_TOKENS.has(token) ? 0.25 : 1; | 82 | LOW_SIGNAL_QUERY_TOKENS.has(token) ? 0.25 : 1; |
| 82 | 83 | ||
| 84 | const namespaceFromPath = (path: string): string => { | ||
| 85 | const [first, second] = path.split("."); | ||
| 86 | return second ? `${first}.${second}` : first; | ||
| 87 | }; | ||
| 88 | |||
| 89 | const sortNamespaces = (namespaces: Iterable<ToolNamespace>): ToolNamespace[] => | ||
| 90 | [...namespaces].sort((left, right) => left.namespace.localeCompare(right.namespace)); | ||
| 91 | |||
| 92 | const loadWorkspaceCatalogNamespaces = (input: { | ||
| 93 | scopeId: Source["scopeId"]; | ||
| 94 | actorScopeId: ScopeId; | ||
| 95 | sourceCatalogStore: Effect.Effect.Success<typeof RuntimeSourceCatalogStoreService>; | ||
| 96 | }): Effect.Effect< | ||
| 97 | readonly ToolNamespace[], | ||
| 98 | Error, | ||
| 99 | ScopeStorageServices | ||
| 100 | > => | ||
| 101 | Effect.gen(function* () { | ||
| 102 | const catalogs = yield* input.sourceCatalogStore.loadWorkspaceSourceCatalogs({ | ||
| 103 | scopeId: input.scopeId, | ||
| 104 | actorScopeId: input.actorScopeId, | ||
| 105 | }); | ||
| 106 | |||
| 107 | const namespaces = new Map<string, ToolNamespace>(); | ||
| 108 | |||
| 109 | for (const catalog of catalogs) { | ||
| 110 | if (!catalog.source.enabled || catalog.source.status !== "connected") { | ||
| 111 | continue; | ||
| 112 | } | ||
| 113 | |||
| 114 | for (const descriptor of Object.values(catalog.projected.toolDescriptors)) { | ||
| 115 | const namespace = namespaceFromPath(descriptor.toolPath.join(".")); | ||
| 116 | const existing = namespaces.get(namespace); | ||
| 117 | namespaces.set(namespace, { | ||
| 118 | namespace, | ||
| 119 | toolCount: (existing?.toolCount ?? 0) + 1, | ||
| 120 | }); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | return sortNamespaces(namespaces.values()); | ||
| 125 | }); | ||
| 126 | |||
| 83 | export const loadWorkspaceCatalogTools = (input: { | 127 | export const loadWorkspaceCatalogTools = (input: { |
| 84 | scopeId: Source["scopeId"]; | 128 | scopeId: Source["scopeId"]; |
| 85 | actorScopeId: ScopeId; | 129 | actorScopeId: ScopeId; |
| @@ -291,9 +335,14 @@ export const createScopeSourceCatalog = (input: { · 205 unmodified lines | |||
| 291 | return { | 335 | return { |
| 292 | listNamespaces: ({ limit }) => | 336 | listNamespaces: ({ limit }) => |
| 293 | provideRuntimeLocalScope( | 337 | provideRuntimeLocalScope( |
| 294 | Effect.flatMap(createSharedCatalog(false), (catalog) => | 338 | provideWorkspaceStorage(Effect.map( |
| 295 | catalog.listNamespaces({ limit }), | 339 | loadWorkspaceCatalogNamespaces({ |
| 296 | ), | 340 | scopeId: input.scopeId, |
| 341 | actorScopeId: input.actorScopeId, | ||
| 342 | sourceCatalogStore: input.sourceCatalogStore, | ||
| 343 | }), | ||
| 344 | (namespaces) => namespaces.slice(0, limit), | ||
| 345 | )), | ||
| 297 | input.runtimeLocalScope, | 346 | input.runtimeLocalScope, |
| 298 | ), | 347 | ), |
| 299 | 348 | ||