Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions apps/hub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,13 @@
"@chat-adapter/state-memory": "^4.35.0",
"@corbits/agent-directory": "workspace:*",
"@corbits/approvals": "workspace:*",
"@corbits/artifact-ui": "workspace:*",
"@corbits/artifacts": "github:corbitsdev/corbits-artifacts#5309a15d4aa504f132c54b4da08bb672b4903873",
"@corbits/codex-provider": "github:corbitsdev/corbits-codex-provider#a1f475f341261c05fa8dade0c82f1246c175b5b3",
"@corbits/cron": "workspace:*",
"@corbits/error-sink": "workspace:*",
"@corbits/inference-catalog": "workspace:*",
"@corbits/mailbox": "github:corbitsdev/corbits-mailbox#e85b81e4a2e0cc318dd33a4f0f92337b6fac7f38",
"@corbits/memory": "github:corbitsdev/corbits-memory#e74da20f148a302dff5400915fe504ee2395e913",
"@corbits/tool-registry-publish": "workspace:*",
"@corbits/url-path": "workspace:*",
"@corbits/webhook-triggers": "workspace:*",
"@corbits/workflows": "workspace:*",
Expand Down
40 changes: 40 additions & 0 deletions apps/hub/src/library-kind-segments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// The hub's own copy of the Files kind-nav mapping, kept in step with
// `apps/web/src/library/kind-filter.ts` by hand: it buckets the same
// `ArtifactListRow`s `@corbits/artifacts`' `countArtifactsBySegments`
// walks, so the per-kind counts the web app's kind nav shows match what
// the web app itself filters client-side.

import type { ArtifactListRow } from "@corbits/artifacts";

export const LIBRARY_KIND_SEGMENTS = ["document", "sheet", "pdf", "routine"] as const;

function titleExtension(title: string): string {
const lower = title.trim().toLowerCase();
const dot = lower.lastIndexOf(".");
if (dot <= 0 || dot === lower.length - 1) return "";
return lower.slice(dot + 1);
}

export function artifactMatchesLibraryKindSegment(
artifact: Pick<ArtifactListRow, "kind" | "title">,
segment: string,
): boolean {
const kind = artifact.kind.trim().toLowerCase();
const ext = titleExtension(artifact.title);

switch (segment) {
case "document":
if (kind === "document") return true;
return kind === "file" && (ext === "doc" || ext === "txt" || ext === "md");
case "sheet":
if (kind === "csv-export" || kind === "sheet") return true;
return kind === "file" && (ext === "xls" || ext === "csv");
case "pdf":
if (kind === "pdf") return true;
return kind === "file" && ext === "pdf";
case "routine":
return kind === "routine";
default:
return false;
}
}
5 changes: 1 addition & 4 deletions apps/hub/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,7 @@ import {
mountWorkflowArtifacts,
type WorkflowArtifactEnv,
} from "@corbits/artifacts";
import {
artifactMatchesLibraryKindSegment,
LIBRARY_KIND_SEGMENTS,
} from "@corbits/artifact-ui/kind-filter";
import { artifactMatchesLibraryKindSegment, LIBRARY_KIND_SEGMENTS } from "./library-kind-segments";
import type { DB } from "@intx/db";
import { sidecar, workflowRun } from "@intx/db/schema";
import path from "node:path";
Expand Down
1 change: 0 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"dependencies": {
"@corbits/agent-directory": "workspace:*",
"@corbits/approvals": "workspace:*",
"@corbits/artifact-ui": "workspace:*",
"@corbits/error-sink": "workspace:*",
"@corbits/myra": "workspace:*",
"@corbits/react-ui": "github:corbitsdev/react-ui#3b122812a307ccb35be31386f7696020c5a84635",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/library/artifact-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { Badge, artifactKindLabel, formatRelativeTime } from "@corbits/react-ui";
import type { ReactNode } from "react";

import type { ArtifactSummary } from "@corbits/artifact-ui/types";
import type { ArtifactSummary } from "./artifact-summary";

export type ArtifactCardMeta = {
readonly sourceChannel?: string | null;
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/library/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from "@corbits/artifact-ui/types";
export * from "./artifact-summary";
export { sortArtifacts, filterArtifacts } from "./sort-filter";
export type { ArtifactSort } from "./sort-filter";
export { ArtifactCard } from "./artifact-card";
Expand All @@ -9,8 +9,8 @@ export {
libraryKindSegmentFromPath,
libraryArtifactPath,
libraryArtifactIdFromPath,
} from "@corbits/artifact-ui/kind-filter";
export type { LibraryKindSegment } from "@corbits/artifact-ui/kind-filter";
} from "./kind-filter";
export type { LibraryKindSegment } from "./kind-filter";
export { workflowRunIdFromSource } from "./provenance";
export {
ARTIFACT_RENDERER_KINDS,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
// Pure helpers for the Files kind nav (`/files`, `/files/document`, …) —
// this package's own names keep the "library" vocabulary (it's the
// this module's own names keep the "library" vocabulary (it's the
// internal name for the artifact store), but `LIBRARY_PATH` tracks the
// app's real mount point: `/library` stays routable only as a redirect
// to `/files` — see `legacy-settings-redirects.tsx`.
// Framework-free so both the web app (rendering the nav + filtering the
// visible list) and the hub (computing honest per-kind counts server-side)
// share one mapping instead of two copies drifting apart.

import { decodedOrNull } from "@corbits/url-path";

import { titleExtension } from "./title-extension";
import type { ArtifactSummary } from "./types";
import type { ArtifactSummary } from "./artifact-summary";

const LIBRARY_PATH = "/files";

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/library/renderer-kind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// the renderer itself never has to know whether it was picked by a Library
// `kind` string or a chat `Part`'s MIME type.

import { titleExtension } from "@corbits/artifact-ui/title-extension";
import { titleExtension } from "./title-extension";

export const ARTIFACT_RENDERER_KINDS = ["doc", "sheet", "pdf", "html", "unsupported"] as const;

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/library/sort-filter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ArtifactSummary } from "@corbits/artifact-ui/types";
import type { ArtifactSummary } from "./artifact-summary";

export type ArtifactSort = "newest" | "oldest";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
// package's names are checked here against the strictest limit.
import { describe, expect, test } from "bun:test";
import path from "node:path";
import { existsSync } from "node:fs";
import { readdir } from "node:fs/promises";
import { encodeToolName } from "@intx/inference";
import { CORBITS_TOOL_PACKAGE_DIRS } from "./registry";

const OPENAI_LIMIT = { provider: "openai", maxLength: 64 } as const;

Expand All @@ -24,10 +25,27 @@ function isBundle(value: unknown): value is Bundle {
);
}

const TOOLS_ROOT = path.resolve(import.meta.dir, "../../../../tools");

async function toolPackageDirs(): Promise<string[]> {
const entries = await readdir(TOOLS_ROOT, { withFileTypes: true });
return entries
.filter(
(entry) =>
entry.isDirectory() && existsSync(path.join(TOOLS_ROOT, entry.name, "package.json")),
)
.map((entry) => entry.name);
}

describe("corbits tool packages fit the 64-char OpenAI-compatible tool-name cap", () => {
for (const dir of CORBITS_TOOL_PACKAGE_DIRS) {
test(path.basename(dir), async () => {
const mod = (await import(path.join(dir, "src", "index.ts"))) as Record<string, unknown>;
test("every tools/* package's qualified tool names encode within the cap", async () => {
const dirs = await toolPackageDirs();
expect(dirs.length).toBeGreaterThan(0);
for (const dir of dirs) {
const mod = (await import(path.join(TOOLS_ROOT, dir, "src", "index.ts"))) as Record<
string,
unknown
>;
const bundles = Object.values(mod).filter(isBundle);
expect(bundles.length).toBeGreaterThan(0);
for (const bundle of bundles) {
Expand All @@ -36,6 +54,6 @@ describe("corbits tool packages fit the 64-char OpenAI-compatible tool-name cap"
expect(() => encodeToolName(qualified, OPENAI_LIMIT)).not.toThrow();
}
}
});
}
}
});
});
Loading
Loading