Archived
feat: expose package management capability
This commit is contained in:
@@ -8,6 +8,7 @@ export const PI_WEB_CAPABILITIES = {
|
||||
sessionsReload: "sessions.reload",
|
||||
promptAttachments: "prompt.attachments",
|
||||
workspaceFileSuggestions: "workspace.fileSuggestions",
|
||||
piPackagesManage: "piPackages.manage",
|
||||
} as const;
|
||||
|
||||
export type PiWebCapability = typeof PI_WEB_CAPABILITIES[keyof typeof PI_WEB_CAPABILITIES];
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { effectivePiWebCapabilities, PI_WEB_CAPABILITIES, SESSIOND_RUNTIME_CAPABILITIES, WEB_RUNTIME_CAPABILITIES, parseKnownPiWebCapabilities } from "./capabilities";
|
||||
|
||||
describe("PI WEB capabilities", () => {
|
||||
it("advertises Pi package management from the web runtime only", () => {
|
||||
expect(WEB_RUNTIME_CAPABILITIES).toContain(PI_WEB_CAPABILITIES.piPackagesManage);
|
||||
expect(SESSIOND_RUNTIME_CAPABILITIES).not.toContain(PI_WEB_CAPABILITIES.piPackagesManage);
|
||||
|
||||
expect(effectivePiWebCapabilities({
|
||||
web: { available: true, capabilities: [PI_WEB_CAPABILITIES.piPackagesManage] },
|
||||
sessiond: { available: false, capabilities: [] },
|
||||
})).toEqual([PI_WEB_CAPABILITIES.piPackagesManage]);
|
||||
});
|
||||
|
||||
it("keeps only known string capabilities when parsing runtime data", () => {
|
||||
expect(parseKnownPiWebCapabilities([PI_WEB_CAPABILITIES.piPackagesManage, "future.capability"])).toEqual([PI_WEB_CAPABILITIES.piPackagesManage]);
|
||||
expect(parseKnownPiWebCapabilities([PI_WEB_CAPABILITIES.piPackagesManage, 1])).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -6,8 +6,23 @@ export type { PiWebCapability };
|
||||
export const KNOWN_PI_WEB_CAPABILITIES = Object.values(PI_WEB_CAPABILITIES);
|
||||
const knownPiWebCapabilities: ReadonlySet<string> = new Set(KNOWN_PI_WEB_CAPABILITIES);
|
||||
|
||||
export const WEB_RUNTIME_CAPABILITIES = [PI_WEB_CAPABILITIES.sessionsDeleteArchived, PI_WEB_CAPABILITIES.sessionsBulkMutations, PI_WEB_CAPABILITIES.sessionsCleanup, PI_WEB_CAPABILITIES.sessionsReload, PI_WEB_CAPABILITIES.promptAttachments, PI_WEB_CAPABILITIES.workspaceFileSuggestions] as const satisfies readonly PiWebCapability[];
|
||||
export const SESSIOND_RUNTIME_CAPABILITIES = [PI_WEB_CAPABILITIES.sessionsDeleteArchived, PI_WEB_CAPABILITIES.sessionsBulkMutations, PI_WEB_CAPABILITIES.sessionsCleanup, PI_WEB_CAPABILITIES.sessionsReload, PI_WEB_CAPABILITIES.promptAttachments] as const satisfies readonly PiWebCapability[];
|
||||
export const WEB_RUNTIME_CAPABILITIES = [
|
||||
PI_WEB_CAPABILITIES.sessionsDeleteArchived,
|
||||
PI_WEB_CAPABILITIES.sessionsBulkMutations,
|
||||
PI_WEB_CAPABILITIES.sessionsCleanup,
|
||||
PI_WEB_CAPABILITIES.sessionsReload,
|
||||
PI_WEB_CAPABILITIES.promptAttachments,
|
||||
PI_WEB_CAPABILITIES.workspaceFileSuggestions,
|
||||
PI_WEB_CAPABILITIES.piPackagesManage,
|
||||
] as const satisfies readonly PiWebCapability[];
|
||||
|
||||
export const SESSIOND_RUNTIME_CAPABILITIES = [
|
||||
PI_WEB_CAPABILITIES.sessionsDeleteArchived,
|
||||
PI_WEB_CAPABILITIES.sessionsBulkMutations,
|
||||
PI_WEB_CAPABILITIES.sessionsCleanup,
|
||||
PI_WEB_CAPABILITIES.sessionsReload,
|
||||
PI_WEB_CAPABILITIES.promptAttachments,
|
||||
] as const satisfies readonly PiWebCapability[];
|
||||
|
||||
const EFFECTIVE_CAPABILITY_REQUIREMENTS = {
|
||||
[PI_WEB_CAPABILITIES.sessionsDeleteArchived]: ["web", "sessiond"],
|
||||
@@ -16,6 +31,7 @@ const EFFECTIVE_CAPABILITY_REQUIREMENTS = {
|
||||
[PI_WEB_CAPABILITIES.sessionsReload]: ["web", "sessiond"],
|
||||
[PI_WEB_CAPABILITIES.promptAttachments]: ["web", "sessiond"],
|
||||
[PI_WEB_CAPABILITIES.workspaceFileSuggestions]: ["web"],
|
||||
[PI_WEB_CAPABILITIES.piPackagesManage]: ["web"],
|
||||
} as const satisfies Record<PiWebCapability, readonly PiWebServiceComponent[]>;
|
||||
|
||||
export function isPiWebCapability(value: unknown): value is PiWebCapability {
|
||||
@@ -26,6 +42,11 @@ export function supportsPiWebCapability(source: { capabilities?: readonly PiWebC
|
||||
return source?.capabilities?.includes(capability) === true;
|
||||
}
|
||||
|
||||
export function parseKnownPiWebCapabilities(value: unknown): PiWebCapability[] | undefined {
|
||||
if (!Array.isArray(value) || !value.every((capability) => typeof capability === "string")) return undefined;
|
||||
return value.filter(isPiWebCapability);
|
||||
}
|
||||
|
||||
export function effectivePiWebCapabilities(components: Partial<Record<PiWebServiceComponent, Pick<PiWebRuntimeComponent, "available" | "capabilities">>>): PiWebCapability[] {
|
||||
return KNOWN_PI_WEB_CAPABILITIES.filter((capability) => {
|
||||
const requiredComponents = EFFECTIVE_CAPABILITY_REQUIREMENTS[capability];
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { PI_WEB_CAPABILITIES } from "./capabilities";
|
||||
import { parsePiWebRuntimeResponse } from "./piWebStatusParsing";
|
||||
|
||||
describe("PI WEB status parsing", () => {
|
||||
it("parses package-management runtime capabilities and ignores unknown string capabilities", () => {
|
||||
expect(parsePiWebRuntimeResponse({
|
||||
packageName: "@jmfederico/pi-web",
|
||||
generatedAt: "now",
|
||||
components: {
|
||||
web: { component: "web", label: "Web/UI", runtimeVersion: "1.0.0", available: true, capabilities: [PI_WEB_CAPABILITIES.piPackagesManage, "future.capability"] },
|
||||
sessiond: { component: "sessiond", label: "Session daemon", runtimeVersion: "1.0.0", available: true, capabilities: ["future.sessiondCapability"] },
|
||||
},
|
||||
capabilities: [PI_WEB_CAPABILITIES.piPackagesManage, "future.capability"],
|
||||
})).toMatchObject({
|
||||
components: {
|
||||
web: { capabilities: [PI_WEB_CAPABILITIES.piPackagesManage] },
|
||||
sessiond: { capabilities: [] },
|
||||
},
|
||||
capabilities: [PI_WEB_CAPABILITIES.piPackagesManage],
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects malformed capability arrays", () => {
|
||||
expect(parsePiWebRuntimeResponse({
|
||||
packageName: "@jmfederico/pi-web",
|
||||
generatedAt: "now",
|
||||
components: {
|
||||
web: { component: "web", label: "Web/UI", available: true, capabilities: [PI_WEB_CAPABILITIES.piPackagesManage, 1] },
|
||||
sessiond: { component: "sessiond", label: "Session daemon", available: true, capabilities: [] },
|
||||
},
|
||||
capabilities: [PI_WEB_CAPABILITIES.piPackagesManage],
|
||||
})).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PiWebCapability, PiWebComponentStatus, PiWebInstallationInfo, PiWebRuntimeComponent, PiWebVersionResponse } from "./apiTypes.js";
|
||||
import { isPiWebCapability } from "./capabilities.js";
|
||||
import type { PiWebComponentStatus, PiWebInstallationInfo, PiWebRuntimeComponent, PiWebRuntimeResponse, PiWebVersionResponse } from "./apiTypes.js";
|
||||
import { parseKnownPiWebCapabilities } from "./capabilities.js";
|
||||
|
||||
export function parsePiWebVersionResponse(value: unknown): PiWebVersionResponse | undefined {
|
||||
if (!isRecord(value)) return undefined;
|
||||
@@ -13,13 +13,26 @@ export function parsePiWebVersionResponse(value: unknown): PiWebVersionResponse
|
||||
return { packageName, generatedAt, components: { web, sessiond } };
|
||||
}
|
||||
|
||||
export function parsePiWebRuntimeResponse(value: unknown): PiWebRuntimeResponse | undefined {
|
||||
if (!isRecord(value)) return undefined;
|
||||
const packageName = value["packageName"];
|
||||
const generatedAt = value["generatedAt"];
|
||||
const components = value["components"];
|
||||
const capabilities = parseKnownPiWebCapabilities(value["capabilities"]);
|
||||
if (typeof packageName !== "string" || packageName === "" || typeof generatedAt !== "string" || generatedAt === "" || !isRecord(components) || capabilities === undefined) return undefined;
|
||||
const web = parsePiWebRuntimeComponent(components["web"]);
|
||||
const sessiond = parsePiWebRuntimeComponent(components["sessiond"]);
|
||||
if (web === undefined || sessiond === undefined) return undefined;
|
||||
return { packageName, generatedAt, components: { web, sessiond }, capabilities };
|
||||
}
|
||||
|
||||
export function parsePiWebRuntimeComponent(value: unknown): PiWebRuntimeComponent | undefined {
|
||||
if (!isRecord(value)) return undefined;
|
||||
const component = value["component"];
|
||||
const label = value["label"];
|
||||
const runtimeVersion = value["runtimeVersion"];
|
||||
const available = value["available"];
|
||||
const capabilities = parsePiWebCapabilities(value["capabilities"]);
|
||||
const capabilities = parseKnownPiWebCapabilities(value["capabilities"]);
|
||||
const error = value["error"];
|
||||
if (component !== "web" && component !== "sessiond") return undefined;
|
||||
if (typeof label !== "string" || label === "" || typeof available !== "boolean" || capabilities === undefined) return undefined;
|
||||
@@ -57,11 +70,6 @@ export function parsePiWebComponentStatus(value: unknown): PiWebComponentStatus
|
||||
};
|
||||
}
|
||||
|
||||
function parsePiWebCapabilities(value: unknown): PiWebCapability[] | undefined {
|
||||
if (!Array.isArray(value) || !value.every(isPiWebCapability)) return undefined;
|
||||
return value;
|
||||
}
|
||||
|
||||
export function parsePiWebInstallationInfo(value: unknown): PiWebInstallationInfo | undefined {
|
||||
if (!isRecord(value)) return undefined;
|
||||
const kind = value["kind"];
|
||||
|
||||
Reference in New Issue
Block a user