feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,147 @@
import {
type LocalSqliteOptionalFeatureRuntimeDatabase,
type ProjectPermission,
ProjectPolicyEngine,
ProjectPolicyUnavailableError,
type SecurityAuditRecord,
type SecurityPolicyDecision,
type SecurityPrincipal,
normalizeSecurityAuditRecord,
normalizeSecurityPrincipal,
} from './authorizationAuthority';
import {
LocalPluginPackagePromptAuthenticationError,
LocalPluginPackagePromptAuthorizationError,
type LocalPluginPackagePromptCommand,
LocalPluginPackagePromptUnavailableError,
} from './contracts';
const STRONG_USER_ASSURANCES = new Set([
'multi_factor',
'hardware',
'local_console',
]);
export const PROMPT_PERMISSIONS = Object.freeze([
'run.start',
'model.invoke',
'secret.use',
] as const satisfies readonly ProjectPermission[]);
function strongUser(
value: Readonly<SecurityPrincipal>,
nowMs: number,
): Readonly<SecurityPrincipal> {
try {
const principal = normalizeSecurityPrincipal(value, nowMs);
if (
principal.subject.type !== 'user' ||
!STRONG_USER_ASSURANCES.has(principal.assurance)
) {
throw new LocalPluginPackagePromptAuthenticationError();
}
return principal;
} catch (error) {
if (error instanceof LocalPluginPackagePromptAuthenticationError) {
throw error;
}
throw new LocalPluginPackagePromptAuthenticationError();
}
}
export function sameFence(
left: NonNullable<SecurityPolicyDecision['fence']>,
right: NonNullable<SecurityPolicyDecision['fence']>,
): boolean {
return (
left.projectVersion === right.projectVersion &&
left.bindingVersion === right.bindingVersion
);
}
export async function authorize(
database: LocalSqliteOptionalFeatureRuntimeDatabase,
principalValue: Readonly<SecurityPrincipal>,
projectId: string,
nowMs: number,
permissions: readonly ProjectPermission[] = PROMPT_PERMISSIONS,
): Promise<
Readonly<{
principal: Readonly<SecurityPrincipal>;
decision: Readonly<SecurityPolicyDecision> & {
readonly fence: NonNullable<SecurityPolicyDecision['fence']>;
};
}>
> {
const principal = strongUser(principalValue, nowMs);
const policy = new ProjectPolicyEngine(database.projectPolicy);
let selected: Readonly<SecurityPolicyDecision> | undefined;
try {
for (const permission of permissions) {
const decision = await policy.authorize(principal, projectId, permission);
if (
decision.effect !== 'allow' ||
!decision.fence ||
decision.fence.bindingVersion === null
) {
throw new LocalPluginPackagePromptAuthorizationError();
}
if (selected?.fence && !sameFence(selected.fence, decision.fence)) {
throw new LocalPluginPackagePromptUnavailableError();
}
selected = decision;
}
} catch (error) {
if (
error instanceof LocalPluginPackagePromptAuthorizationError ||
error instanceof LocalPluginPackagePromptUnavailableError
) {
throw error;
}
if (error instanceof ProjectPolicyUnavailableError) {
throw new LocalPluginPackagePromptUnavailableError({ cause: error });
}
throw new LocalPluginPackagePromptUnavailableError({
cause: error instanceof Error ? error : undefined,
});
}
if (!selected?.fence || selected.fence.bindingVersion === null) {
throw new LocalPluginPackagePromptUnavailableError();
}
return Object.freeze({
principal,
decision: Object.freeze({
...selected,
fence: Object.freeze({
projectVersion: selected.fence.projectVersion,
bindingVersion: selected.fence.bindingVersion,
}),
}),
});
}
export function allowedAudit(
command: Readonly<LocalPluginPackagePromptCommand>,
principal: Readonly<SecurityPrincipal>,
decision: Readonly<SecurityPolicyDecision> & {
readonly fence: NonNullable<SecurityPolicyDecision['fence']>;
},
occurredAtMs: number,
): Readonly<SecurityAuditRecord> {
return normalizeSecurityAuditRecord({
eventId: command.request.auditEventId,
requestId: command.request.requestId,
operationId:
command.operation === 'prompt.execution.inspect'
? 'prompt.execution.read'
: command.operation === 'prompt.execution.output.read'
? 'prompt.execution.output.read'
: command.operation,
projectId: command.request.projectId,
subject: principal.subject,
authenticationId: principal.authenticationId,
outcome: 'allowed',
reasons: decision.reasons,
fence: decision.fence,
occurredAtMs,
});
}
@@ -0,0 +1,13 @@
export {
ProjectPolicyEngine,
ProjectPolicyUnavailableError,
} from '@qinglong/runtime-core/project-policy';
export type { ProjectPermission } from '@qinglong/runtime-core/project-policy';
export { normalizeSecurityPrincipal } from '@qinglong/runtime-core/security';
export type {
SecurityPolicyDecision,
SecurityPrincipal,
} from '@qinglong/runtime-core/security';
export { normalizeSecurityAuditRecord } from '@qinglong/runtime-core/security-audit';
export type { SecurityAuditRecord } from '@qinglong/runtime-core/security-audit';
export type { LocalSqliteOptionalFeatureRuntimeDatabase } from '@qinglong/local-sqlite/optional-feature-runtime';
@@ -0,0 +1,531 @@
import path from 'node:path';
import {
PrivateLocalCommandFileError,
readPrivateLocalCommandFile,
} from './codecAuthority';
import {
LocalPluginPackagePromptCommandConfigurationError,
type LocalPluginPackagePromptCommand,
type LocalPluginPackagePromptCommandOptions,
type LocalPluginPackagePromptInspectCommandOptions,
type LocalPluginPackagePromptOutputCommandOptions,
type LocalPluginPackagePromptOutputIntent,
} from './contracts';
const MAX_PATH_BYTES = 4_096;
const MAX_TIMEOUT_MS = 10 * 60_000;
const MAX_PARAMETER_COUNT = 128;
const MAX_PARAMETER_VALUE_BYTES = 64 * 1_024;
const MIN_OUTPUT_RETENTION_MS = 60 * 60_000;
const MAX_OUTPUT_RETENTION_MS = 365 * 24 * 60 * 60_000;
const UUID_V4_PATTERN =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
const IDENTITY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
const PACKAGE_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
const RESOURCE_ID_PATTERN = /^[a-z][a-z0-9-]{0,62}$/;
function exactObject(
value: unknown,
required: readonly string[],
optional: readonly string[],
label: string,
): asserts value is Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new LocalPluginPackagePromptCommandConfigurationError(
`${label} must be an object`,
);
}
const keys = Object.keys(value).sort();
const allowed = new Set([...required, ...optional]);
if (
required.some((key) => !keys.includes(key)) ||
keys.some((key) => !allowed.has(key))
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
`${label} shape is invalid`,
);
}
}
function boundedPath(value: unknown, label: string): string {
if (
typeof value !== 'string' ||
!path.isAbsolute(value) ||
path.parse(value).root === value ||
path.normalize(value) !== value ||
value.includes('\0') ||
Buffer.byteLength(value, 'utf8') > MAX_PATH_BYTES
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
`${label} must be a normalized bounded absolute non-root path`,
);
}
return value;
}
function descendant(root: string, candidate: string, label: string): void {
const relative = path.relative(root, candidate);
if (
relative.length === 0 ||
relative === '..' ||
relative.startsWith(`..${path.sep}`) ||
path.isAbsolute(relative)
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
`${label} must be a descendant of deploymentRoot`,
);
}
}
function identity(value: unknown, label: string): string {
if (typeof value !== 'string' || !IDENTITY_PATTERN.test(value)) {
throw new LocalPluginPackagePromptCommandConfigurationError(
`${label} is invalid`,
);
}
return value;
}
function normalizeOptions(
value: unknown,
operation: 'prompt.inspect' | 'prompt.execution.inspect',
): Readonly<LocalPluginPackagePromptInspectCommandOptions>;
function normalizeOptions(
value: unknown,
operation: 'prompt.execution.output.read',
): Readonly<LocalPluginPackagePromptOutputCommandOptions>;
function normalizeOptions(
value: unknown,
operation: 'prompt.execute',
): Readonly<LocalPluginPackagePromptCommandOptions>;
function normalizeOptions(
value: unknown,
operation:
| 'prompt.inspect'
| 'prompt.execution.inspect'
| 'prompt.execution.output.read'
| 'prompt.execute',
): Readonly<
| LocalPluginPackagePromptCommandOptions
| LocalPluginPackagePromptInspectCommandOptions
| LocalPluginPackagePromptOutputCommandOptions
> {
const execution = operation === 'prompt.execute';
const outputRead = operation === 'prompt.execution.output.read';
exactObject(
value,
[
'credentialFilePath',
'databasePath',
'deploymentRoot',
'ownerPepperKeyringDirectory',
'profile',
...(execution ? ['providerAuthorityFilePath', 'secretKeyringPath'] : []),
...(outputRead ? ['promptOutputKeyringPath'] : []),
],
['busyTimeoutMs', ...(execution ? ['promptOutputKeyringPath'] : [])],
'options',
);
const deploymentRoot = boundedPath(value.deploymentRoot, 'deploymentRoot');
const basePaths = {
databasePath: boundedPath(value.databasePath, 'databasePath'),
ownerPepperKeyringDirectory: boundedPath(
value.ownerPepperKeyringDirectory,
'ownerPepperKeyringDirectory',
),
credentialFilePath: boundedPath(
value.credentialFilePath,
'credentialFilePath',
),
};
const executionPaths = execution
? {
secretKeyringPath: boundedPath(
value.secretKeyringPath,
'secretKeyringPath',
),
providerAuthorityFilePath: boundedPath(
value.providerAuthorityFilePath,
'providerAuthorityFilePath',
),
}
: {};
const outputPaths =
value.promptOutputKeyringPath === undefined
? {}
: {
promptOutputKeyringPath: boundedPath(
value.promptOutputKeyringPath,
'promptOutputKeyringPath',
),
};
const paths = { ...basePaths, ...executionPaths, ...outputPaths };
for (const [label, candidate] of Object.entries(paths)) {
descendant(deploymentRoot, candidate, label);
}
if (value.profile !== 'edge' && value.profile !== 'standalone') {
throw new LocalPluginPackagePromptCommandConfigurationError(
'profile must be edge or standalone',
);
}
if (
value.busyTimeoutMs !== undefined &&
(!Number.isSafeInteger(value.busyTimeoutMs) ||
(value.busyTimeoutMs as number) < 100 ||
(value.busyTimeoutMs as number) > 30_000)
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'busyTimeoutMs is invalid',
);
}
return Object.freeze({
deploymentRoot,
...paths,
profile: value.profile,
...(value.busyTimeoutMs === undefined
? {}
: { busyTimeoutMs: value.busyTimeoutMs as number }),
});
}
function normalizeParameters(value: unknown): Readonly<Record<string, string>> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
Object.getPrototypeOf(value) !== Object.prototype
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'parameters must be a plain object',
);
}
const entries = Object.entries(value);
if (
entries.length > MAX_PARAMETER_COUNT ||
entries.some(
([key, candidate]) =>
!RESOURCE_ID_PATTERN.test(key) ||
typeof candidate !== 'string' ||
Buffer.byteLength(candidate, 'utf8') > MAX_PARAMETER_VALUE_BYTES,
)
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'parameters are invalid or exceed the bounded input budget',
);
}
return Object.freeze(Object.fromEntries(entries));
}
function normalizeOutputIntent(
value: unknown,
): Readonly<LocalPluginPackagePromptOutputIntent> {
exactObject(value, ['mode'], ['retentionPolicy'], 'output');
if (value.mode === 'live_only') {
exactObject(value, ['mode'], [], 'output');
return Object.freeze({ mode: 'live_only' as const });
}
if (value.mode !== 'durable_artifact') {
throw new LocalPluginPackagePromptCommandConfigurationError(
'output mode is invalid',
);
}
exactObject(value, ['mode', 'retentionPolicy'], [], 'output');
exactObject(
value.retentionPolicy,
['retentionMs', 'revision'],
[],
'output.retentionPolicy',
);
if (
!Number.isSafeInteger(value.retentionPolicy.retentionMs) ||
(value.retentionPolicy.retentionMs as number) < MIN_OUTPUT_RETENTION_MS ||
(value.retentionPolicy.retentionMs as number) > MAX_OUTPUT_RETENTION_MS
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'output retention is invalid',
);
}
return Object.freeze({
mode: 'durable_artifact' as const,
retentionPolicy: Object.freeze({
revision: identity(
value.retentionPolicy.revision,
'output.retentionPolicy.revision',
),
retentionMs: value.retentionPolicy.retentionMs as number,
}),
});
}
function normalizeCommand(
value: unknown,
): Readonly<LocalPluginPackagePromptCommand> {
exactObject(
value,
['operation', 'options', 'request', 'schemaVersion'],
[],
'command',
);
if (
value.schemaVersion !== 1 ||
(value.operation !== 'prompt.inspect' &&
value.operation !== 'prompt.execution.inspect' &&
value.operation !== 'prompt.execution.output.read' &&
value.operation !== 'prompt.execute')
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'command version or operation is invalid',
);
}
if (value.operation === 'prompt.inspect') {
const options = normalizeOptions(value.options, 'prompt.inspect');
exactObject(
value.request,
[
'auditEventId',
'failureAuditEventId',
'packageName',
'projectId',
'requestId',
],
[],
'request',
);
if (
typeof value.request.packageName !== 'string' ||
!PACKAGE_NAME_PATTERN.test(value.request.packageName) ||
typeof value.request.auditEventId !== 'string' ||
!UUID_V4_PATTERN.test(value.request.auditEventId) ||
typeof value.request.failureAuditEventId !== 'string' ||
!UUID_V4_PATTERN.test(value.request.failureAuditEventId) ||
value.request.auditEventId === value.request.failureAuditEventId
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'request value is invalid',
);
}
return Object.freeze({
schemaVersion: 1,
operation: 'prompt.inspect',
options,
request: Object.freeze({
projectId: identity(value.request.projectId, 'projectId'),
packageName: value.request.packageName,
requestId: identity(value.request.requestId, 'requestId'),
auditEventId: value.request.auditEventId,
failureAuditEventId: value.request.failureAuditEventId,
}),
});
}
if (value.operation === 'prompt.execution.inspect') {
const options = normalizeOptions(value.options, 'prompt.execution.inspect');
exactObject(
value.request,
[
'auditEventId',
'executionRequestId',
'failureAuditEventId',
'packageName',
'projectId',
'promptId',
'requestId',
],
[],
'request',
);
if (
typeof value.request.packageName !== 'string' ||
!PACKAGE_NAME_PATTERN.test(value.request.packageName) ||
typeof value.request.promptId !== 'string' ||
!RESOURCE_ID_PATTERN.test(value.request.promptId) ||
typeof value.request.auditEventId !== 'string' ||
!UUID_V4_PATTERN.test(value.request.auditEventId) ||
typeof value.request.failureAuditEventId !== 'string' ||
!UUID_V4_PATTERN.test(value.request.failureAuditEventId) ||
value.request.auditEventId === value.request.failureAuditEventId
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'request value is invalid',
);
}
return Object.freeze({
schemaVersion: 1,
operation: 'prompt.execution.inspect',
options,
request: Object.freeze({
projectId: identity(value.request.projectId, 'projectId'),
packageName: value.request.packageName,
promptId: value.request.promptId,
executionRequestId: identity(
value.request.executionRequestId,
'executionRequestId',
),
requestId: identity(value.request.requestId, 'requestId'),
auditEventId: value.request.auditEventId,
failureAuditEventId: value.request.failureAuditEventId,
}),
});
}
if (value.operation === 'prompt.execution.output.read') {
const options = normalizeOptions(
value.options,
'prompt.execution.output.read',
);
exactObject(
value.request,
[
'auditEventId',
'executionRequestId',
'failureAuditEventId',
'packageName',
'projectId',
'promptId',
'requestId',
],
[],
'request',
);
if (
typeof value.request.packageName !== 'string' ||
!PACKAGE_NAME_PATTERN.test(value.request.packageName) ||
typeof value.request.promptId !== 'string' ||
!RESOURCE_ID_PATTERN.test(value.request.promptId) ||
typeof value.request.auditEventId !== 'string' ||
!UUID_V4_PATTERN.test(value.request.auditEventId) ||
typeof value.request.failureAuditEventId !== 'string' ||
!UUID_V4_PATTERN.test(value.request.failureAuditEventId) ||
value.request.auditEventId === value.request.failureAuditEventId
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'request value is invalid',
);
}
return Object.freeze({
schemaVersion: 1,
operation: 'prompt.execution.output.read',
options,
request: Object.freeze({
projectId: identity(value.request.projectId, 'projectId'),
packageName: value.request.packageName,
promptId: value.request.promptId,
executionRequestId: identity(
value.request.executionRequestId,
'executionRequestId',
),
requestId: identity(value.request.requestId, 'requestId'),
auditEventId: value.request.auditEventId,
failureAuditEventId: value.request.failureAuditEventId,
}),
});
}
const options = normalizeOptions(value.options, 'prompt.execute');
const hasTemperature =
!!value.request &&
typeof value.request === 'object' &&
!Array.isArray(value.request) &&
Object.hasOwn(value.request, 'temperature');
exactObject(
value.request,
[
'auditEventId',
'failureAuditEventId',
'maxOutputTokens',
'model',
'output',
'packageName',
'parameters',
'projectId',
'promptId',
'provider',
'requestId',
'timeoutMs',
'traceId',
],
hasTemperature ? ['temperature'] : [],
'request',
);
const request = value.request;
if (
typeof request.packageName !== 'string' ||
!PACKAGE_NAME_PATTERN.test(request.packageName) ||
typeof request.promptId !== 'string' ||
!RESOURCE_ID_PATTERN.test(request.promptId) ||
typeof request.auditEventId !== 'string' ||
!UUID_V4_PATTERN.test(request.auditEventId) ||
typeof request.failureAuditEventId !== 'string' ||
!UUID_V4_PATTERN.test(request.failureAuditEventId) ||
request.auditEventId === request.failureAuditEventId ||
!Number.isSafeInteger(request.maxOutputTokens) ||
(request.maxOutputTokens as number) < 1 ||
(request.maxOutputTokens as number) > 1_000_000 ||
!Number.isSafeInteger(request.timeoutMs) ||
(request.timeoutMs as number) < 100 ||
(request.timeoutMs as number) > MAX_TIMEOUT_MS ||
(request.temperature !== undefined &&
(typeof request.temperature !== 'number' ||
!Number.isFinite(request.temperature) ||
request.temperature < 0 ||
request.temperature > 2))
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'request value is invalid',
);
}
const output = normalizeOutputIntent(request.output);
if (
output.mode === 'durable_artifact' &&
options.promptOutputKeyringPath === undefined
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'durable output requires promptOutputKeyringPath',
);
}
if (
output.mode === 'live_only' &&
options.promptOutputKeyringPath !== undefined
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'live-only output must not configure promptOutputKeyringPath',
);
}
return Object.freeze({
schemaVersion: 1,
operation: 'prompt.execute',
options,
request: Object.freeze({
projectId: identity(request.projectId, 'projectId'),
packageName: request.packageName,
promptId: request.promptId,
requestId: identity(request.requestId, 'requestId'),
traceId: identity(request.traceId, 'traceId'),
auditEventId: request.auditEventId,
failureAuditEventId: request.failureAuditEventId,
parameters: normalizeParameters(request.parameters),
provider: identity(request.provider, 'provider'),
model: identity(request.model, 'model'),
maxOutputTokens: request.maxOutputTokens as number,
...(request.temperature === undefined
? {}
: { temperature: request.temperature as number }),
timeoutMs: request.timeoutMs as number,
output,
}),
});
}
export function readCommandFile(
candidatePath: string,
): Readonly<LocalPluginPackagePromptCommand> {
try {
return normalizeCommand(readPrivateLocalCommandFile(candidatePath));
} catch (error) {
if (error instanceof LocalPluginPackagePromptCommandConfigurationError) {
throw error;
}
throw new LocalPluginPackagePromptCommandConfigurationError(
'command file cannot be read',
error instanceof PrivateLocalCommandFileError ? error : undefined,
);
}
}
@@ -0,0 +1,4 @@
export {
PrivateLocalCommandFileError,
readPrivateLocalCommandFile,
} from '@qinglong/local-command-file';
@@ -0,0 +1,10 @@
export type { PluginPackagePromptCatalogItem } from '@qinglong/ai/plugin-package-prompt-catalog';
export type { PluginPackagePromptExecutionInspection } from '@qinglong/ai/plugin-package-prompt-execution-inspection';
export type { PluginPackagePromptExecutionOutputReadResult } from '@qinglong/ai/plugin-package-prompt-execution-output-read';
export type { PluginPackagePromptOutputArtifactReference } from '@qinglong/ai/plugin-package-prompt-output-artifact';
export type { ModelGatewayProviderAuthority } from '@qinglong/ai/profile';
export type { establishAuthenticatedLocalCommand } from '@qinglong/local-owner-console/authenticated-command';
export type {
LocalSqliteOptionalFeatureRuntimeDatabase,
openLocalSqliteOptionalFeatureRuntimeDatabase,
} from '@qinglong/local-sqlite/optional-feature-runtime';
@@ -0,0 +1,251 @@
import type {
establishAuthenticatedLocalCommand,
LocalSqliteOptionalFeatureRuntimeDatabase,
ModelGatewayProviderAuthority,
openLocalSqliteOptionalFeatureRuntimeDatabase,
PluginPackagePromptCatalogItem,
PluginPackagePromptExecutionInspection,
PluginPackagePromptExecutionOutputReadResult,
PluginPackagePromptOutputArtifactReference,
} from './contractAuthority';
export interface LocalPluginPackagePromptInspectCommandOptions {
readonly deploymentRoot: string;
readonly databasePath: string;
readonly profile: 'edge' | 'standalone';
readonly ownerPepperKeyringDirectory: string;
readonly credentialFilePath: string;
readonly busyTimeoutMs?: number;
}
export interface LocalPluginPackagePromptCommandOptions
extends LocalPluginPackagePromptInspectCommandOptions {
readonly secretKeyringPath: string;
readonly providerAuthorityFilePath: string;
readonly promptOutputKeyringPath?: string;
}
export interface LocalPluginPackagePromptOutputCommandOptions
extends LocalPluginPackagePromptInspectCommandOptions {
readonly promptOutputKeyringPath: string;
}
export type LocalPluginPackagePromptOutputIntent =
| Readonly<{ mode: 'live_only' }>
| Readonly<{
mode: 'durable_artifact';
retentionPolicy: Readonly<{
revision: string;
retentionMs: number;
}>;
}>;
export interface ExecuteLocalPluginPackagePromptCommand {
readonly schemaVersion: 1;
readonly operation: 'prompt.execute';
readonly options: LocalPluginPackagePromptCommandOptions;
readonly request: Readonly<{
projectId: string;
packageName: string;
promptId: string;
requestId: string;
traceId: string;
auditEventId: string;
failureAuditEventId: string;
parameters: Readonly<Record<string, string>>;
provider: string;
model: string;
maxOutputTokens: number;
temperature?: number;
timeoutMs: number;
output: Readonly<LocalPluginPackagePromptOutputIntent>;
}>;
}
export interface InspectLocalPluginPackagePromptCommand {
readonly schemaVersion: 1;
readonly operation: 'prompt.inspect';
readonly options: LocalPluginPackagePromptInspectCommandOptions;
readonly request: Readonly<{
projectId: string;
packageName: string;
requestId: string;
auditEventId: string;
failureAuditEventId: string;
}>;
}
export interface InspectLocalPluginPackagePromptExecutionCommand {
readonly schemaVersion: 1;
readonly operation: 'prompt.execution.inspect';
readonly options: LocalPluginPackagePromptInspectCommandOptions;
readonly request: Readonly<{
projectId: string;
packageName: string;
promptId: string;
executionRequestId: string;
requestId: string;
auditEventId: string;
failureAuditEventId: string;
}>;
}
export interface ReadLocalPluginPackagePromptExecutionOutputCommand {
readonly schemaVersion: 1;
readonly operation: 'prompt.execution.output.read';
readonly options: LocalPluginPackagePromptOutputCommandOptions;
readonly request: Readonly<{
projectId: string;
packageName: string;
promptId: string;
executionRequestId: string;
requestId: string;
auditEventId: string;
failureAuditEventId: string;
}>;
}
export type LocalPluginPackagePromptCommand =
| ExecuteLocalPluginPackagePromptCommand
| InspectLocalPluginPackagePromptCommand
| InspectLocalPluginPackagePromptExecutionCommand
| ReadLocalPluginPackagePromptExecutionOutputCommand;
export type LocalPluginPackagePromptCommandResult =
| Readonly<{
schemaVersion: 1;
operation: 'prompt.inspect';
projectId: string;
packageName: string;
found: boolean;
publicationState: 'active' | 'withdrawn' | 'absent' | null;
prompts: readonly Readonly<PluginPackagePromptCatalogItem>[];
}>
| Readonly<{
schemaVersion: 1;
operation: 'prompt.execution.inspect';
projectId: string;
packageName: string;
promptId: string;
executionRequestId: string;
found: boolean;
execution: Readonly<PluginPackagePromptExecutionInspection> | null;
}>
| Readonly<{
schemaVersion: 1;
operation: 'prompt.execution.output.read';
projectId: string;
packageName: string;
promptId: string;
executionRequestId: string;
status: 'not_found';
}>
| Readonly<{
schemaVersion: 1;
operation: 'prompt.execution.output.read';
projectId: string;
packageName: string;
promptId: string;
executionRequestId: string;
status: 'available';
reference: Readonly<PluginPackagePromptOutputArtifactReference>;
result: Extract<
PluginPackagePromptExecutionOutputReadResult,
{
status: 'available';
}
>['result'];
}>
| Readonly<{
schemaVersion: 1;
operation: 'prompt.execute';
status: 'executed' | 'resumed' | 'existing';
projectId: string;
packageName: string;
promptId: string;
requestId: string;
invocationId: string;
runId: string;
stepRunId: string;
planDigest: string;
receiptDigest: string;
finalizationDigest: string;
runStatus: 'succeeded' | 'failed' | 'cancelled' | 'timed_out';
result: Readonly<{
provider: string;
model: string;
text: string;
finishReason: string;
usage: Readonly<{
inputTokens: number;
outputTokens: number;
totalTokens: number;
costMicros?: number;
}>;
}> | null;
outputArtifact?: Readonly<PluginPackagePromptOutputArtifactReference>;
}>;
export interface LocalPluginPackagePromptCommandRunner {
run(commandFilePath: string): Promise<LocalPluginPackagePromptCommandResult>;
}
export interface LocalPluginPackagePromptCommandRunnerDependencies {
readonly openDatabase: typeof openLocalSqliteOptionalFeatureRuntimeDatabase;
readonly authenticate: typeof establishAuthenticatedLocalCommand;
readonly loadProviders: (
options: Readonly<{
database: LocalSqliteOptionalFeatureRuntimeDatabase;
command: Readonly<ExecuteLocalPluginPackagePromptCommand>;
now: () => number;
}>,
) => Promise<ModelGatewayProviderAuthority>;
readonly now: () => number;
}
export class LocalPluginPackagePromptCommandConfigurationError extends TypeError {
readonly code = 'LOCAL_PLUGIN_PACKAGE_PROMPT_COMMAND_CONFIGURATION_INVALID';
constructor(message: string, readonly cause?: unknown) {
super(
`Local Plugin Package Prompt command configuration is invalid: ${message}`,
);
this.name = 'LocalPluginPackagePromptCommandConfigurationError';
}
}
export class LocalPluginPackagePromptAuthenticationError extends Error {
readonly code = 'LOCAL_PLUGIN_PACKAGE_PROMPT_AUTHENTICATION_REQUIRED';
constructor() {
super('Local Plugin Package Prompt requires a current strong User');
this.name = 'LocalPluginPackagePromptAuthenticationError';
}
}
export class LocalPluginPackagePromptAuthorizationError extends Error {
readonly code = 'LOCAL_PLUGIN_PACKAGE_PROMPT_FORBIDDEN';
constructor() {
super('Local Plugin Package Prompt is not authorized');
this.name = 'LocalPluginPackagePromptAuthorizationError';
}
}
export class LocalPluginPackagePromptNotFoundError extends Error {
readonly code = 'LOCAL_PLUGIN_PACKAGE_PROMPT_NOT_FOUND';
constructor() {
super('Active Plugin Package Prompt is not available');
this.name = 'LocalPluginPackagePromptNotFoundError';
}
}
export class LocalPluginPackagePromptUnavailableError extends Error {
readonly code = 'LOCAL_PLUGIN_PACKAGE_PROMPT_UNAVAILABLE';
constructor(options?: ErrorOptions) {
super('Local Plugin Package Prompt is unavailable', options);
this.name = 'LocalPluginPackagePromptUnavailableError';
}
}
@@ -0,0 +1,103 @@
import {
type ActiveModelGatewayCapability,
BoundModelProviderCredentialProvider,
EncryptedLocalSecretService,
LocalModelProviderCredentialRepository,
LocalSecretKeyringFileProvider,
type LocalSqliteOptionalFeatureRuntimeDatabase,
type ModelGatewayProviderAuthority,
type PluginPackagePromptExecutionPlan,
loadProjectedModelGatewayProviderAuthority,
} from './supportAuthority';
import {
type ExecuteLocalPluginPackagePromptCommand,
LocalPluginPackagePromptCommandConfigurationError,
type LocalPluginPackagePromptCommandRunnerDependencies,
LocalPluginPackagePromptUnavailableError,
} from './contracts';
export async function defaultLoadProviders(
input: Readonly<{
database: LocalSqliteOptionalFeatureRuntimeDatabase;
command: Readonly<ExecuteLocalPluginPackagePromptCommand>;
now: () => number;
}>,
): Promise<ModelGatewayProviderAuthority> {
const credentials = new LocalModelProviderCredentialRepository(
input.database.authority,
{ now: input.now },
);
const secrets = new EncryptedLocalSecretService(
input.database.localSecrets,
new LocalSecretKeyringFileProvider(input.command.options.secretKeyringPath),
);
return loadProjectedModelGatewayProviderAuthority({
configFile: input.command.options.providerAuthorityFilePath,
credentials: new BoundModelProviderCredentialProvider({
bindings: credentials,
audit: credentials,
secrets,
now: input.now,
}),
});
}
export function dependencies(
value: LocalPluginPackagePromptCommandRunnerDependencies,
): Readonly<LocalPluginPackagePromptCommandRunnerDependencies> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
Object.keys(value).sort().join('\0') !==
['authenticate', 'loadProviders', 'now', 'openDatabase']
.sort()
.join('\0') ||
typeof value.openDatabase !== 'function' ||
typeof value.authenticate !== 'function' ||
typeof value.loadProviders !== 'function' ||
typeof value.now !== 'function'
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'runner dependencies are invalid',
);
}
return Object.freeze({ ...value });
}
export async function stopGateway(
capability: ActiveModelGatewayCapability | undefined,
): Promise<void> {
if (!capability) return;
for (let attempt = 0; attempt < 8; attempt += 1) {
if ((await capability.stop()) === 'stopped') return;
await new Promise<void>((resolve) => setImmediate(resolve));
}
throw new LocalPluginPackagePromptUnavailableError();
}
export function assertReplayRequest(
plan: Readonly<PluginPackagePromptExecutionPlan>,
command: Readonly<ExecuteLocalPluginPackagePromptCommand>,
): void {
const request = command.request;
if (
plan.target.projectId !== request.projectId ||
plan.target.packageName !== request.packageName ||
plan.target.promptId !== request.promptId ||
plan.traceId !== request.traceId ||
plan.provider !== request.provider ||
plan.model !== request.model ||
plan.maxOutputTokens !== request.maxOutputTokens ||
plan.temperature !== (request.temperature ?? null) ||
plan.output?.mode !== request.output.mode ||
(request.output.mode === 'durable_artifact' &&
(plan.output?.mode !== 'durable_artifact' ||
JSON.stringify(plan.output.retentionPolicy) !==
JSON.stringify(request.output.retentionPolicy)))
) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'request conflicts with the durable Prompt plan',
);
}
}
@@ -0,0 +1,535 @@
import {
type ActiveModelGatewayCapability,
type AuthenticatedLocalCommand,
type AuthorizedPluginPackagePromptExecutionInspection,
LocalModelInvocationFeatureActivationRepository,
LocalModelInvocationRepository,
LocalModelPriceCatalogRepository,
LocalPluginPackagePromptAdmissionRepository,
LocalPluginPackagePromptExecutionInspectionRepository,
LocalPluginPackagePromptExecutionOutputReferenceRepository,
LocalPluginPackagePromptOutputArtifactRepository,
LocalPluginPackagePromptOutputRetentionRepository,
type LocalSqliteAuthenticatedUserCredentialFence,
LocalSqliteAuthenticatedManagementFenceError,
PluginPackagePromptExecutionInspectionAuthorizationFenceConflictError,
type PluginPackagePromptExecutionPlan,
PluginPackagePromptExecutionOutputReadService,
PluginPackagePromptExecutor,
type PluginPackagePromptOutputArtifactReadAuthorizer,
type PluginPackagePromptOutputCompletionCapability,
PluginPackagePromptOutputCompletionCoordinator,
PluginPackagePromptOutputFileKeyring,
PluginPackagePromptOutputReadService,
ProjectPolicyEngine,
assertLocalModelInvocationFeatureActive,
bootstrapModelGatewayProfile,
commitLocalSqliteSecurityAuditInTransaction,
confirmLocalSqliteAuthenticatedUserCredentialFence,
confirmLocalSqliteProjectPolicyFence,
createPluginPackagePromptCatalogResult,
establishAuthenticatedLocalCommand,
normalizeSecurityAuditRecord,
openLocalSqliteOptionalFeatureRuntimeDatabase,
} from './runnerAuthority';
import {
LocalPluginPackagePromptAuthenticationError,
LocalPluginPackagePromptAuthorizationError,
type LocalPluginPackagePromptCommandResult,
LocalPluginPackagePromptCommandConfigurationError,
type LocalPluginPackagePromptCommandRunner,
type LocalPluginPackagePromptCommandRunnerDependencies,
LocalPluginPackagePromptNotFoundError,
LocalPluginPackagePromptUnavailableError,
} from './contracts';
import { readCommandFile } from './codec';
import {
PROMPT_PERMISSIONS,
allowedAudit,
authorize,
sameFence,
} from './authorization';
import {
assertReplayRequest,
defaultLoadProviders,
dependencies,
stopGateway,
} from './executionSupport';
export function createLocalPluginPackagePromptCommandRunner(
candidateDependencies: LocalPluginPackagePromptCommandRunnerDependencies = {
openDatabase: openLocalSqliteOptionalFeatureRuntimeDatabase,
authenticate: establishAuthenticatedLocalCommand,
loadProviders: defaultLoadProviders,
now: Date.now,
},
): LocalPluginPackagePromptCommandRunner {
const adapters = dependencies(candidateDependencies);
return Object.freeze({
async run(commandFilePath: string) {
const command = readCommandFile(commandFilePath);
const database = await adapters.openDatabase({
databasePath: command.options.databasePath,
profile: command.options.profile,
...(command.options.busyTimeoutMs === undefined
? {}
: { busyTimeoutMs: command.options.busyTimeoutMs }),
});
let capability: ActiveModelGatewayCapability | undefined;
let featureReady = false;
let authenticated: Readonly<AuthenticatedLocalCommand> | undefined;
let authorization: Awaited<ReturnType<typeof authorize>> | undefined;
try {
const activation =
command.operation === 'prompt.execute'
? assertLocalModelInvocationFeatureActive(database.authority.client)
: null;
featureReady = true;
authenticated = await adapters.authenticate(database, {
deploymentRoot: command.options.deploymentRoot,
databasePath: command.options.databasePath,
ownerPepperKeyringDirectory:
command.options.ownerPepperKeyringDirectory,
credentialFilePath: command.options.credentialFilePath,
authenticationNamespace: 'local_plugin_package_prompt',
});
await authenticated.confirm();
const nowMs = adapters.now();
if (!Number.isSafeInteger(nowMs) || nowMs < 0) {
throw new LocalPluginPackagePromptCommandConfigurationError(
'clock is invalid',
);
}
authorization = await authorize(
database,
authenticated.principal,
command.request.projectId,
nowMs,
command.operation === 'prompt.inspect'
? Object.freeze(['model.invoke'] as const)
: command.operation === 'prompt.execution.inspect'
? Object.freeze(['run.read'] as const)
: command.operation === 'prompt.execution.output.read'
? Object.freeze(['artifact.read'] as const)
: PROMPT_PERMISSIONS,
);
const authorized = authorization;
const credentialFence =
authenticated.databaseFence as Readonly<LocalSqliteAuthenticatedUserCredentialFence>;
const audit = allowedAudit(
command,
authorized.principal,
authorized.decision,
nowMs,
);
if (command.operation === 'prompt.inspect') {
await database.securityAudit.record(audit);
const publication = await database.automationPublications.findCurrent(
command.request.projectId,
command.request.packageName,
);
const catalog = createPluginPackagePromptCatalogResult(
command.request.projectId,
command.request.packageName,
publication,
);
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
projectId: catalog.projectId,
packageName: catalog.packageName,
found: catalog.found,
publicationState: catalog.publicationState,
prompts: catalog.prompts,
});
}
if (command.operation === 'prompt.execution.inspect') {
const inspections =
new LocalPluginPackagePromptExecutionInspectionRepository(
database.authority,
Object.freeze({
confirm(
inspection: Readonly<AuthorizedPluginPackagePromptExecutionInspection>,
auditReplay: boolean,
) {
try {
if (
inspection.actor.type !==
authorized.principal.subject.type ||
inspection.actor.id !== authorized.principal.subject.id ||
inspection.projectId !== command.request.projectId ||
!sameFence(inspection.fence, authorized.decision.fence)
) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
confirmLocalSqliteAuthenticatedUserCredentialFence(
database.authority,
credentialFence,
);
confirmLocalSqliteProjectPolicyFence(
database.authority,
inspection.projectId,
inspection.actor,
inspection.fence,
);
commitLocalSqliteSecurityAuditInTransaction(
database.authority,
inspection.audit,
auditReplay,
);
} catch {
throw new PluginPackagePromptExecutionInspectionAuthorizationFenceConflictError();
}
},
}),
);
const result = await inspections.inspectAuthorized({
projectId: command.request.projectId,
packageName: command.request.packageName,
promptId: command.request.promptId,
executionRequestId: command.request.executionRequestId,
actor: authorized.principal.subject,
fence: authorized.decision.fence,
audit,
});
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
projectId: result.projectId,
packageName: result.packageName,
promptId: result.promptId,
executionRequestId: result.executionRequestId,
found: result.found,
execution: result.execution,
});
}
if (command.operation === 'prompt.execution.output.read') {
await database.securityAudit.record(audit);
const policy = new ProjectPolicyEngine(database.projectPolicy);
const outputReader = new PluginPackagePromptOutputReadService({
artifacts: new LocalPluginPackagePromptOutputArtifactRepository(
database.authority,
),
authorizer: Object.freeze({
async authorize(
request: Parameters<
PluginPackagePromptOutputArtifactReadAuthorizer['authorize']
>[0],
) {
const decision = await policy.authorize(
request.principal,
request.projectId,
'artifact.read',
);
return decision.effect === 'allow'
? Object.freeze({ effect: 'allow' as const })
: Object.freeze({
effect: decision.effect,
reasonCode: 'artifact_read_denied',
});
},
}),
retention: new LocalPluginPackagePromptOutputRetentionRepository(
database.authority,
),
keys: new PluginPackagePromptOutputFileKeyring(
command.options.promptOutputKeyringPath,
),
now: adapters.now,
});
const result =
await new PluginPackagePromptExecutionOutputReadService({
references:
new LocalPluginPackagePromptExecutionOutputReferenceRepository(
database.authority,
),
outputs: outputReader,
}).read({
principal: authorized.principal,
projectId: command.request.projectId,
packageName: command.request.packageName,
promptId: command.request.promptId,
executionRequestId: command.request.executionRequestId,
});
const target = Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
projectId: result.projectId,
packageName: result.packageName,
promptId: result.promptId,
executionRequestId: result.executionRequestId,
});
return result.status === 'not_found'
? Object.freeze({ ...target, status: 'not_found' as const })
: Object.freeze({
...target,
status: 'available' as const,
reference: result.reference,
result: result.result,
});
}
const admissions = new LocalPluginPackagePromptAdmissionRepository(
database.authority,
Object.freeze({
confirm(
plan: Readonly<PluginPackagePromptExecutionPlan>,
replay: boolean,
) {
if (
plan.requestedBySubject.type !==
authorized.principal.subject.type ||
plan.requestedBySubject.id !==
authorized.principal.subject.id ||
plan.target.projectId !== command.request.projectId ||
!sameFence(plan.policyFence, authorized.decision.fence)
) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
confirmLocalSqliteAuthenticatedUserCredentialFence(
database.authority,
credentialFence,
);
confirmLocalSqliteProjectPolicyFence(
database.authority,
plan.target.projectId,
plan.requestedBySubject,
plan.policyFence,
);
commitLocalSqliteSecurityAuditInTransaction(
database.authority,
audit,
replay,
);
},
}),
);
const existing = await admissions.findPlanByRequestId(
command.request.requestId,
);
if (existing) assertReplayRequest(existing, command);
const publication = existing
? await database.automationPublications.findByDigest(
existing.target.publicationDigest,
)
: await database.automationPublications.findCurrent(
command.request.projectId,
command.request.packageName,
);
if (
!publication ||
publication.state !== 'active' ||
publication.target.projectId !== command.request.projectId ||
publication.target.packageName !== command.request.packageName ||
!publication.definitions.prompts.some(
({ id }) => id === command.request.promptId,
)
) {
throw new LocalPluginPackagePromptNotFoundError();
}
if (
existing &&
(!sameFence(existing.policyFence, authorization.decision.fence) ||
existing.requestedBySubject.type !==
authorization.principal.subject.type ||
existing.requestedBySubject.id !==
authorization.principal.subject.id)
) {
throw new LocalPluginPackagePromptAuthorizationError();
}
const repository = new LocalModelInvocationRepository(
database.authority,
);
const pricing = new LocalModelPriceCatalogRepository(
database.authority,
);
let durableOutput:
| PluginPackagePromptOutputCompletionCapability
| undefined;
const outputKeys =
command.request.output.mode === 'durable_artifact'
? new PluginPackagePromptOutputFileKeyring(
command.options.promptOutputKeyringPath!,
)
: undefined;
const gateway = await bootstrapModelGatewayProfile({
enabled: true,
profile: command.options.profile,
loadStorage: async () =>
Object.freeze({
repository,
pricing,
close: async () => undefined,
}),
loadProviders: () =>
adapters.loadProviders({
database,
command,
now: adapters.now,
}),
...(outputKeys === undefined
? {}
: {
createSuccessfulCompletion: (coordinator) => {
durableOutput =
new PluginPackagePromptOutputCompletionCoordinator({
coordinator,
keys: outputKeys,
now: adapters.now,
});
return durableOutput;
},
}),
confirmActive: async () => {
await database.authority.enqueue(
async () => {
const current =
new LocalModelInvocationFeatureActivationRepository(
database.authority.client,
).findCurrent();
if (
activation === null ||
current?.state !== 'active' ||
current.generation !== activation.generation ||
current.transitionDigest !== activation.transitionDigest
) {
throw new LocalPluginPackagePromptUnavailableError();
}
},
() => new LocalPluginPackagePromptUnavailableError(),
);
},
audit: async () => undefined,
maxConcurrent: 1,
recoveryLimit: command.options.profile === 'edge' ? 4 : 16,
now: adapters.now,
});
if (gateway.status !== 'active') {
throw new LocalPluginPackagePromptUnavailableError();
}
capability = gateway.capability;
const executor = new PluginPackagePromptExecutor({
admissions,
invocations: repository,
gateway: capability,
...(durableOutput === undefined ? {} : { durableOutput }),
});
const plannedAtMs = existing?.plannedAtMs ?? nowMs;
const deadlineAtMs =
existing?.deadlineAtMs ?? plannedAtMs + command.request.timeoutMs;
const executed = await executor.execute({
publication,
expectedPublicationDigest: publication.publicationDigest,
promptId: command.request.promptId,
requestId: command.request.requestId,
traceId: command.request.traceId,
requestedBySubject: authorization.principal.subject,
policyFence: authorization.decision.fence as Readonly<{
projectVersion: number;
bindingVersion: number;
}>,
parameters: command.request.parameters,
provider: command.request.provider,
model: command.request.model,
maxOutputTokens: command.request.maxOutputTokens,
...(command.request.temperature === undefined
? {}
: { temperature: command.request.temperature }),
plannedAtMs,
deadlineAtMs,
output: command.request.output,
});
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
status: executed.status,
projectId: command.request.projectId,
packageName: command.request.packageName,
promptId: command.request.promptId,
requestId: executed.admission.requestId,
invocationId: executed.admission.invocationId,
runId: executed.admission.runId,
stepRunId: executed.admission.stepRunId,
planDigest: executed.admission.planDigest,
receiptDigest: executed.admission.receiptDigest,
finalizationDigest: executed.finalization.receiptDigest,
runStatus: executed.finalization.runStatus,
result: executed.result,
...(executed.outputArtifact === undefined
? {}
: { outputArtifact: executed.outputArtifact }),
});
} catch (error) {
if (featureReady) {
const occurredAtMs = adapters.now();
if (!Number.isSafeInteger(occurredAtMs) || occurredAtMs < 0) {
throw new LocalPluginPackagePromptUnavailableError();
}
const outcome = !authenticated
? ('authentication_rejected' as const)
: error instanceof LocalPluginPackagePromptAuthorizationError
? ('denied' as const)
: ('authorization_unavailable' as const);
try {
await database.securityAudit.record(
normalizeSecurityAuditRecord({
eventId: command.request.failureAuditEventId,
requestId: command.request.requestId,
operationId: command.operation,
projectId: command.request.projectId,
subject: authenticated?.principal.subject ?? null,
authenticationId:
authenticated?.principal.authenticationId ?? null,
outcome,
reasons: [
!authenticated
? 'strong_authentication_required'
: outcome === 'denied'
? 'permission_missing'
: command.operation === 'prompt.inspect'
? 'prompt_catalog_unavailable'
: command.operation === 'prompt.execution.inspect'
? 'prompt_execution_inspection_unavailable'
: command.operation === 'prompt.execution.output.read'
? 'prompt_execution_output_read_unavailable'
: 'prompt_execution_unavailable',
],
fence: authorization?.decision.fence ?? null,
occurredAtMs,
}),
);
} catch (auditError) {
throw new LocalPluginPackagePromptUnavailableError({
cause: auditError instanceof Error ? auditError : undefined,
});
}
}
if (
error instanceof LocalPluginPackagePromptCommandConfigurationError ||
error instanceof LocalPluginPackagePromptAuthenticationError ||
error instanceof LocalPluginPackagePromptAuthorizationError ||
error instanceof LocalPluginPackagePromptNotFoundError
) {
throw error;
}
throw new LocalPluginPackagePromptUnavailableError({
cause: error instanceof Error ? error : undefined,
});
} finally {
let stopError: unknown;
try {
await stopGateway(capability);
} catch (error) {
stopError = error;
}
await database.close();
if (stopError) throw stopError;
}
},
});
}
export function runLocalPluginPackagePromptCommandFile(
commandFilePath: string,
): Promise<LocalPluginPackagePromptCommandResult> {
return createLocalPluginPackagePromptCommandRunner().run(commandFilePath);
}
@@ -0,0 +1,36 @@
export {
LocalModelInvocationFeatureActivationRepository,
assertLocalModelInvocationFeatureActive,
} from '@qinglong/ai/local-feature-activation';
export { LocalModelInvocationRepository } from '@qinglong/ai/local-model-invocation-storage';
export { LocalModelPriceCatalogRepository } from '@qinglong/ai/local-price-catalog-storage';
export { LocalPluginPackagePromptAdmissionRepository } from '@qinglong/ai/local-plugin-package-prompt-admission-storage';
export { LocalPluginPackagePromptExecutionInspectionRepository } from '@qinglong/ai/local-plugin-package-prompt-execution-inspection';
export { LocalPluginPackagePromptExecutionOutputReferenceRepository } from '@qinglong/ai/local-plugin-package-prompt-execution-output-reference-storage';
export { LocalPluginPackagePromptOutputArtifactRepository } from '@qinglong/ai/local-plugin-package-prompt-output-artifact-storage';
export { LocalPluginPackagePromptOutputRetentionRepository } from '@qinglong/ai/local-plugin-package-prompt-output-retention-storage';
export { PluginPackagePromptExecutor } from '@qinglong/ai/plugin-package-prompt-executor';
export { createPluginPackagePromptCatalogResult } from '@qinglong/ai/plugin-package-prompt-catalog';
export { PluginPackagePromptExecutionInspectionAuthorizationFenceConflictError } from '@qinglong/ai/plugin-package-prompt-execution-inspection';
export type { AuthorizedPluginPackagePromptExecutionInspection } from '@qinglong/ai/plugin-package-prompt-execution-inspection';
export { PluginPackagePromptOutputCompletionCoordinator } from '@qinglong/ai/plugin-package-prompt-output-completion';
export type { PluginPackagePromptOutputCompletionCapability } from '@qinglong/ai/plugin-package-prompt-output-completion';
export { PluginPackagePromptOutputFileKeyring } from '@qinglong/ai/plugin-package-prompt-output-file-keyring';
export { PluginPackagePromptOutputReadService } from '@qinglong/ai/plugin-package-prompt-output-read';
export { PluginPackagePromptExecutionOutputReadService } from '@qinglong/ai/plugin-package-prompt-execution-output-read';
export type { PluginPackagePromptExecutionPlan } from '@qinglong/ai/plugin-package-prompt-execution';
export type { PluginPackagePromptOutputArtifactReadAuthorizer } from '@qinglong/ai/plugin-package-prompt-output-artifact';
export { bootstrapModelGatewayProfile } from '@qinglong/ai/profile';
export type { ActiveModelGatewayCapability } from '@qinglong/ai/profile';
export { establishAuthenticatedLocalCommand } from '@qinglong/local-owner-console/authenticated-command';
export type { AuthenticatedLocalCommand } from '@qinglong/local-owner-console/authenticated-command';
export {
commitLocalSqliteSecurityAuditInTransaction,
confirmLocalSqliteAuthenticatedUserCredentialFence,
confirmLocalSqliteProjectPolicyFence,
LocalSqliteAuthenticatedManagementFenceError,
openLocalSqliteOptionalFeatureRuntimeDatabase,
} from '@qinglong/local-sqlite/optional-feature-runtime';
export type { LocalSqliteAuthenticatedUserCredentialFence } from '@qinglong/local-sqlite/optional-feature-runtime';
export { ProjectPolicyEngine } from '@qinglong/runtime-core/project-policy';
export { normalizeSecurityAuditRecord } from '@qinglong/runtime-core/security-audit';
@@ -0,0 +1,13 @@
export { LocalModelProviderCredentialRepository } from '@qinglong/ai/local-model-provider-credential-storage';
export type { PluginPackagePromptExecutionPlan } from '@qinglong/ai/plugin-package-prompt-execution';
export { BoundModelProviderCredentialProvider } from '@qinglong/ai/provider-credential';
export { loadProjectedModelGatewayProviderAuthority } from '@qinglong/ai/projected-model-gateway-authority';
export type {
ActiveModelGatewayCapability,
ModelGatewayProviderAuthority,
} from '@qinglong/ai/profile';
export {
EncryptedLocalSecretService,
LocalSecretKeyringFileProvider,
} from '@qinglong/local-secret';
export type { LocalSqliteOptionalFeatureRuntimeDatabase } from '@qinglong/local-sqlite/optional-feature-runtime';
@@ -0,0 +1,435 @@
import path from 'node:path';
import {
PrivateLocalCommandFileError,
readPrivateLocalCommandFile,
} from './codecAuthority';
import {
type CancelLocalPluginPackageWorkflowCommand,
type InspectLocalPluginPackageWorkflowRunCommand,
type ListLocalPluginPackageWorkflowRunEventsCommand,
type ListLocalPluginPackageWorkflowRunsCommand,
type ListLocalPluginPackageWorkflowStepRunsCommand,
type LocalPluginPackageWorkflowCommand,
LocalPluginPackageWorkflowCommandConfigurationError,
type LocalPluginPackageWorkflowCommandOptions,
type LocalPluginPackageWorkflowCommandRequestBase,
type StartLocalPluginPackageWorkflowCommand,
} from './contracts';
const MAX_PATH_BYTES = 4_096;
const UUID_V4_PATTERN =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
function exactObject(
value: unknown,
required: readonly string[],
optional: readonly string[],
label: string,
): asserts value is Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
`${label} must be an object`,
);
}
const keys = Object.keys(value).sort();
const allowed = new Set([...required, ...optional]);
if (
required.some((key) => !keys.includes(key)) ||
keys.some((key) => !allowed.has(key))
) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
`${label} shape is invalid`,
);
}
}
function boundedPath(value: unknown, label: string): string {
if (
typeof value !== 'string' ||
!path.isAbsolute(value) ||
path.parse(value).root === value ||
path.normalize(value) !== value ||
value.includes('\0') ||
Buffer.byteLength(value, 'utf8') > MAX_PATH_BYTES
) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
`${label} must be a normalized bounded absolute non-root path`,
);
}
return value;
}
function descendant(root: string, candidate: string, label: string): void {
const relative = path.relative(root, candidate);
if (
relative.length === 0 ||
relative === '..' ||
relative.startsWith(`..${path.sep}`) ||
path.isAbsolute(relative)
) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
`${label} must be a descendant of deploymentRoot`,
);
}
}
function normalizeOptions(
value: unknown,
): Readonly<LocalPluginPackageWorkflowCommandOptions> {
exactObject(
value,
[
'credentialFilePath',
'databasePath',
'deploymentRoot',
'ownerPepperKeyringDirectory',
'profile',
],
['busyTimeoutMs'],
'options',
);
const deploymentRoot = boundedPath(value.deploymentRoot, 'deploymentRoot');
const databasePath = boundedPath(value.databasePath, 'databasePath');
const ownerPepperKeyringDirectory = boundedPath(
value.ownerPepperKeyringDirectory,
'ownerPepperKeyringDirectory',
);
const credentialFilePath = boundedPath(
value.credentialFilePath,
'credentialFilePath',
);
descendant(deploymentRoot, databasePath, 'databasePath');
descendant(
deploymentRoot,
ownerPepperKeyringDirectory,
'ownerPepperKeyringDirectory',
);
descendant(deploymentRoot, credentialFilePath, 'credentialFilePath');
if (value.profile !== 'edge' && value.profile !== 'standalone') {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'profile must be edge or standalone',
);
}
if (
value.busyTimeoutMs !== undefined &&
(!Number.isSafeInteger(value.busyTimeoutMs) ||
(value.busyTimeoutMs as number) < 100 ||
(value.busyTimeoutMs as number) > 30_000)
) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'busyTimeoutMs is invalid',
);
}
return Object.freeze({
deploymentRoot,
databasePath,
profile: value.profile,
ownerPepperKeyringDirectory,
credentialFilePath,
...(value.busyTimeoutMs === undefined
? {}
: { busyTimeoutMs: value.busyTimeoutMs as number }),
});
}
function normalizedRequestBase(
value: Record<string, unknown>,
): LocalPluginPackageWorkflowCommandRequestBase {
for (const key of ['projectId', 'packageName', 'requestId'] as const) {
if (typeof value[key] !== 'string') {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
`${key} is invalid`,
);
}
}
for (const key of ['auditEventId', 'failureAuditEventId'] as const) {
if (typeof value[key] !== 'string' || !UUID_V4_PATTERN.test(value[key])) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
`${key} must be a UUID v4`,
);
}
}
if (value.auditEventId === value.failureAuditEventId) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'audit identities must be distinct',
);
}
return value as unknown as LocalPluginPackageWorkflowCommandRequestBase;
}
function normalizeCommand(
value: unknown,
): Readonly<LocalPluginPackageWorkflowCommand> {
exactObject(
value,
['operation', 'options', 'request', 'schemaVersion'],
[],
'command',
);
if (
value.schemaVersion !== 1 ||
(value.operation !== 'workflow.inspect' &&
value.operation !== 'workflow.run.inspect' &&
value.operation !== 'workflow.run.list' &&
value.operation !== 'workflow.step.list' &&
value.operation !== 'workflow.event.list' &&
value.operation !== 'workflow.start' &&
value.operation !== 'workflow.cancel')
) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'command version or operation is invalid',
);
}
const options = normalizeOptions(value.options);
if (value.operation === 'workflow.inspect') {
exactObject(
value.request,
[
'auditEventId',
'failureAuditEventId',
'packageName',
'projectId',
'requestId',
],
[],
'request',
);
return Object.freeze({
schemaVersion: 1,
operation: value.operation,
options,
request: normalizedRequestBase(value.request),
});
}
if (value.operation === 'workflow.run.inspect') {
exactObject(
value.request,
[
'auditEventId',
'failureAuditEventId',
'packageName',
'projectId',
'requestId',
'runId',
'workflowId',
],
[],
'request',
);
return Object.freeze({
schemaVersion: 1,
operation: value.operation,
options,
request: normalizedRequestBase(
value.request,
) as InspectLocalPluginPackageWorkflowRunCommand['request'],
});
}
if (value.operation === 'workflow.run.list') {
exactObject(
value.request,
[
'after',
'auditEventId',
'failureAuditEventId',
'limit',
'packageName',
'projectId',
'requestId',
'workflowId',
],
[],
'request',
);
if (
!Number.isSafeInteger(value.request.limit) ||
(value.request.limit as number) < 1 ||
(value.request.limit as number) > 64
) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'run list limit is invalid',
);
}
if (value.request.after !== null) {
exactObject(
value.request.after,
['admittedAtMs', 'runId'],
[],
'request.after',
);
if (
!Number.isSafeInteger(value.request.after.admittedAtMs) ||
(value.request.after.admittedAtMs as number) < 0 ||
typeof value.request.after.runId !== 'string' ||
!UUID_V4_PATTERN.test(value.request.after.runId)
) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'run list cursor is invalid',
);
}
}
return Object.freeze({
schemaVersion: 1,
operation: value.operation,
options,
request: normalizedRequestBase(
value.request,
) as ListLocalPluginPackageWorkflowRunsCommand['request'],
});
}
if (value.operation === 'workflow.step.list') {
exactObject(
value.request,
[
'after',
'auditEventId',
'failureAuditEventId',
'limit',
'packageName',
'projectId',
'requestId',
'runId',
'workflowId',
],
[],
'request',
);
if (
!Number.isSafeInteger(value.request.limit) ||
(value.request.limit as number) < 1 ||
(value.request.limit as number) > 64
) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'StepRun list limit is invalid',
);
}
if (value.request.after !== null) {
exactObject(value.request.after, ['id', 'stepKey'], [], 'request.after');
if (
typeof value.request.after.id !== 'string' ||
typeof value.request.after.stepKey !== 'string'
) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'StepRun list cursor is invalid',
);
}
}
return Object.freeze({
schemaVersion: 1,
operation: value.operation,
options,
request: normalizedRequestBase(
value.request,
) as ListLocalPluginPackageWorkflowStepRunsCommand['request'],
});
}
if (value.operation === 'workflow.event.list') {
exactObject(
value.request,
[
'afterSequence',
'auditEventId',
'failureAuditEventId',
'limit',
'packageName',
'projectId',
'requestId',
'runId',
'workflowId',
],
[],
'request',
);
if (
!Number.isSafeInteger(value.request.limit) ||
(value.request.limit as number) < 1 ||
(value.request.limit as number) > 64 ||
!Number.isSafeInteger(value.request.afterSequence) ||
(value.request.afterSequence as number) < 0
) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'RunEvent list page is invalid',
);
}
return Object.freeze({
schemaVersion: 1,
operation: value.operation,
options,
request: normalizedRequestBase(
value.request,
) as ListLocalPluginPackageWorkflowRunEventsCommand['request'],
});
}
if (value.operation === 'workflow.cancel') {
exactObject(
value.request,
[
'auditEventId',
'failureAuditEventId',
'mutationId',
'packageName',
'projectId',
'requestId',
'runEventId',
'runId',
],
[],
'request',
);
return Object.freeze({
schemaVersion: 1,
operation: value.operation,
options,
request: normalizedRequestBase(
value.request,
) as CancelLocalPluginPackageWorkflowCommand['request'],
});
}
exactObject(
value.request,
[
'auditEventId',
'failureAuditEventId',
'packageName',
'planId',
'projectId',
'requestId',
'runId',
'stepRunIds',
'workflowId',
],
[],
'request',
);
return Object.freeze({
schemaVersion: 1,
operation: value.operation,
options,
request: normalizedRequestBase(
value.request,
) as StartLocalPluginPackageWorkflowCommand['request'],
});
}
export function readCommandFile(
candidatePath: string,
): Readonly<LocalPluginPackageWorkflowCommand> {
try {
return normalizeCommand(readPrivateLocalCommandFile(candidatePath));
} catch (error) {
if (error instanceof LocalPluginPackageWorkflowCommandConfigurationError) {
throw error;
}
if (error instanceof PrivateLocalCommandFileError) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'command file cannot be read',
error,
);
}
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'command file cannot be read',
error,
);
}
}
@@ -0,0 +1,4 @@
export {
PrivateLocalCommandFileError,
readPrivateLocalCommandFile,
} from '@qinglong/local-command-file';
@@ -0,0 +1,9 @@
export type {
PluginPackageWorkflowRunEventListResult,
PluginPackageWorkflowRunInspectionResult,
PluginPackageWorkflowRunListResult,
PluginPackageWorkflowStepRunListResult,
} from '@qinglong/runtime-core/plugin-package-workflow-administration';
export type { createLocalPluginPackageWorkflowAdministrationService } from '@qinglong/local-admin/plugin-package-workflow-administration';
export type { establishAuthenticatedLocalCommand } from '@qinglong/local-owner-console/authenticated-command';
export type { openLocalSqlitePluginPackageWorkflowAdministrationDatabase } from '@qinglong/local-sqlite/plugin-package-workflow-administration';
@@ -0,0 +1,232 @@
import type {
createLocalPluginPackageWorkflowAdministrationService,
establishAuthenticatedLocalCommand,
openLocalSqlitePluginPackageWorkflowAdministrationDatabase,
PluginPackageWorkflowRunEventListResult,
PluginPackageWorkflowRunInspectionResult,
PluginPackageWorkflowRunListResult,
PluginPackageWorkflowStepRunListResult,
} from './contractAuthority';
export interface LocalPluginPackageWorkflowCommandOptions {
readonly deploymentRoot: string;
readonly databasePath: string;
readonly profile: 'edge' | 'standalone';
readonly ownerPepperKeyringDirectory: string;
readonly credentialFilePath: string;
readonly busyTimeoutMs?: number;
}
export interface LocalPluginPackageWorkflowCommandRequestBase {
readonly projectId: string;
readonly packageName: string;
readonly requestId: string;
readonly auditEventId: string;
readonly failureAuditEventId: string;
}
export interface InspectLocalPluginPackageWorkflowCommand {
readonly schemaVersion: 1;
readonly operation: 'workflow.inspect';
readonly options: LocalPluginPackageWorkflowCommandOptions;
readonly request: LocalPluginPackageWorkflowCommandRequestBase;
}
export interface StartLocalPluginPackageWorkflowCommand {
readonly schemaVersion: 1;
readonly operation: 'workflow.start';
readonly options: LocalPluginPackageWorkflowCommandOptions;
readonly request: LocalPluginPackageWorkflowCommandRequestBase & {
readonly workflowId: string;
readonly planId: string;
readonly runId: string;
readonly stepRunIds: Readonly<Record<string, string>>;
};
}
export interface CancelLocalPluginPackageWorkflowCommand {
readonly schemaVersion: 1;
readonly operation: 'workflow.cancel';
readonly options: LocalPluginPackageWorkflowCommandOptions;
readonly request: LocalPluginPackageWorkflowCommandRequestBase & {
readonly runId: string;
readonly mutationId: string;
readonly runEventId: string;
};
}
export interface InspectLocalPluginPackageWorkflowRunCommand {
readonly schemaVersion: 1;
readonly operation: 'workflow.run.inspect';
readonly options: LocalPluginPackageWorkflowCommandOptions;
readonly request: LocalPluginPackageWorkflowCommandRequestBase & {
readonly workflowId: string;
readonly runId: string;
};
}
export interface ListLocalPluginPackageWorkflowRunsCommand {
readonly schemaVersion: 1;
readonly operation: 'workflow.run.list';
readonly options: LocalPluginPackageWorkflowCommandOptions;
readonly request: LocalPluginPackageWorkflowCommandRequestBase & {
readonly workflowId: string;
readonly limit: number;
readonly after: Readonly<{ admittedAtMs: number; runId: string }> | null;
};
}
export interface ListLocalPluginPackageWorkflowStepRunsCommand {
readonly schemaVersion: 1;
readonly operation: 'workflow.step.list';
readonly options: LocalPluginPackageWorkflowCommandOptions;
readonly request: LocalPluginPackageWorkflowCommandRequestBase & {
readonly workflowId: string;
readonly runId: string;
readonly limit: number;
readonly after: Readonly<{ stepKey: string; id: string }> | null;
};
}
export interface ListLocalPluginPackageWorkflowRunEventsCommand {
readonly schemaVersion: 1;
readonly operation: 'workflow.event.list';
readonly options: LocalPluginPackageWorkflowCommandOptions;
readonly request: LocalPluginPackageWorkflowCommandRequestBase & {
readonly workflowId: string;
readonly runId: string;
readonly limit: number;
readonly afterSequence: number;
};
}
export type LocalPluginPackageWorkflowCommand =
| InspectLocalPluginPackageWorkflowCommand
| InspectLocalPluginPackageWorkflowRunCommand
| ListLocalPluginPackageWorkflowRunsCommand
| ListLocalPluginPackageWorkflowStepRunsCommand
| ListLocalPluginPackageWorkflowRunEventsCommand
| StartLocalPluginPackageWorkflowCommand
| CancelLocalPluginPackageWorkflowCommand;
export type LocalPluginPackageWorkflowCommandResult =
| Readonly<{
schemaVersion: 1;
operation: 'workflow.inspect';
projectId: string;
packageName: string;
found: boolean;
publicationState: 'active' | 'withdrawn' | 'absent' | null;
workflows: readonly Readonly<{
id: string;
name: string;
enabled: boolean;
steps: readonly Readonly<{
id: string;
task: string;
needs: readonly string[];
}>[];
}>[];
}>
| Readonly<{
schemaVersion: 1;
operation: 'workflow.run.inspect';
projectId: string;
packageName: string;
workflowId: string;
runId: string;
found: boolean;
run: PluginPackageWorkflowRunInspectionResult['run'];
stepCount: number | null;
stepStatusCounts: PluginPackageWorkflowRunInspectionResult['stepStatusCounts'];
}>
| Readonly<{
schemaVersion: 1;
operation: 'workflow.run.list';
projectId: string;
packageName: string;
workflowId: string;
after: PluginPackageWorkflowRunListResult['after'];
runs: PluginPackageWorkflowRunListResult['runs'];
truncated: boolean;
next: PluginPackageWorkflowRunListResult['next'];
}>
| Readonly<{
schemaVersion: 1;
operation: 'workflow.step.list';
projectId: string;
packageName: string;
workflowId: string;
runId: string;
found: boolean;
stepRuns: PluginPackageWorkflowStepRunListResult['stepRuns'];
truncated: boolean;
next: PluginPackageWorkflowStepRunListResult['next'];
}>
| Readonly<{
schemaVersion: 1;
operation: 'workflow.event.list';
projectId: string;
packageName: string;
workflowId: string;
runId: string;
found: boolean;
afterSequence: number;
headSequence: number | null;
events: PluginPackageWorkflowRunEventListResult['events'];
truncated: boolean;
nextAfterSequence: number | null;
}>
| Readonly<{
schemaVersion: 1;
operation: 'workflow.start';
status: 'created' | 'existing';
projectId: string;
packageName: string;
workflowId: string;
runId: string;
stepCount: number;
admittedAtMs: number;
}>
| Readonly<{
schemaVersion: 1;
operation: 'workflow.cancel';
status:
| 'accepted'
| 'existing'
| 'already_requested'
| 'already_terminal';
projectId: string;
packageName: string;
workflowId: string;
runId: string;
runStatus: string;
runVersion: number;
eventSequence: number;
cancelRequestedAtMs?: number;
cancelReason?: string;
}>;
export interface LocalPluginPackageWorkflowCommandRunner {
run(
commandFilePath: string,
): Promise<LocalPluginPackageWorkflowCommandResult>;
}
export interface LocalPluginPackageWorkflowCommandRunnerDependencies {
readonly openDatabase: typeof openLocalSqlitePluginPackageWorkflowAdministrationDatabase;
readonly authenticate: typeof establishAuthenticatedLocalCommand;
readonly createService: typeof createLocalPluginPackageWorkflowAdministrationService;
readonly now: () => number;
}
export class LocalPluginPackageWorkflowCommandConfigurationError extends TypeError {
readonly code = 'LOCAL_PLUGIN_PACKAGE_WORKFLOW_COMMAND_CONFIGURATION_INVALID';
constructor(message: string, readonly cause?: unknown) {
super(
`Local Plugin Package Workflow command configuration is invalid: ${message}`,
);
this.name = 'LocalPluginPackageWorkflowCommandConfigurationError';
}
}
@@ -0,0 +1,132 @@
import {
type AuthenticatedLocalCommand,
AuthenticatedLocalCommandAuthenticationError,
InvalidPluginPackageWorkflowExecutionPlanError,
LocalPluginPackageWorkflowAdministrationAuthenticationError,
LocalPluginPackageWorkflowAdministrationAuthorizationError,
LocalPluginPackageWorkflowAdministrationConfigurationError,
LocalPluginPackageWorkflowAdministrationNotFoundError,
LocalPluginPackageWorkflowAdministrationUnavailableError,
type LocalSqliteAuthenticatedUserCredentialFence,
LocalSqliteAuthenticatedManagementFenceError,
type LocalSqlitePluginPackageWorkflowAdministrationDatabase,
PluginPackageWorkflowAdministrationAuthorizationFenceConflictError,
PluginPackageWorkflowAdministrationMutationConflictError,
PluginPackageWorkflowAdmissionConflictError,
PluginPackageWorkflowAdmissionNotAllowedError,
type SecurityAuditRecord,
} from './supportAuthority';
import {
type LocalPluginPackageWorkflowCommand,
LocalPluginPackageWorkflowCommandConfigurationError,
type LocalPluginPackageWorkflowCommandRunnerDependencies,
} from './contracts';
export function failureAudit(
command: Readonly<LocalPluginPackageWorkflowCommand>,
authenticated: Readonly<AuthenticatedLocalCommand> | undefined,
error: unknown,
occurredAtMs: number,
): Readonly<SecurityAuditRecord> | null {
if (
error instanceof
LocalPluginPackageWorkflowAdministrationAuthenticationError ||
error instanceof
LocalPluginPackageWorkflowAdministrationAuthorizationError ||
error instanceof LocalPluginPackageWorkflowAdministrationUnavailableError
) {
return null;
}
let outcome: SecurityAuditRecord['outcome'];
let reason: string;
if (
!authenticated ||
error instanceof AuthenticatedLocalCommandAuthenticationError
) {
outcome = 'authentication_rejected';
reason = 'credential_rejected';
} else if (
error instanceof LocalSqliteAuthenticatedManagementFenceError ||
error instanceof
PluginPackageWorkflowAdministrationAuthorizationFenceConflictError
) {
outcome = 'denied';
reason = 'credential_or_policy_fence_rejected';
} else if (
error instanceof PluginPackageWorkflowAdministrationMutationConflictError ||
error instanceof PluginPackageWorkflowAdmissionConflictError
) {
outcome = 'denied';
reason = 'workflow_admission_conflict';
} else if (
error instanceof LocalPluginPackageWorkflowAdministrationNotFoundError ||
error instanceof
LocalPluginPackageWorkflowAdministrationConfigurationError ||
error instanceof LocalPluginPackageWorkflowCommandConfigurationError ||
error instanceof InvalidPluginPackageWorkflowExecutionPlanError ||
error instanceof PluginPackageWorkflowAdmissionNotAllowedError
) {
outcome = 'denied';
reason = 'workflow_admission_rejected';
} else {
return null;
}
return Object.freeze({
eventId: command.request.failureAuditEventId,
requestId: command.request.requestId,
operationId:
command.operation === 'workflow.start'
? 'workflow.start'
: command.operation === 'workflow.cancel'
? 'workflow.cancel'
: command.operation === 'workflow.step.list'
? 'workflow.step.list'
: command.operation === 'workflow.event.list'
? 'workflow.event.list'
: command.operation === 'workflow.run.list'
? 'workflow.run.list'
: command.operation === 'workflow.run.inspect'
? 'workflow.run.read'
: 'workflow.read',
projectId: command.request.projectId,
subject: authenticated?.principal.subject ?? null,
authenticationId: authenticated?.principal.authenticationId ?? null,
outcome,
reasons: Object.freeze([reason]),
fence: null,
occurredAtMs,
});
}
export function dependencies(
value: LocalPluginPackageWorkflowCommandRunnerDependencies,
): Readonly<LocalPluginPackageWorkflowCommandRunnerDependencies> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
Object.keys(value).sort().join('\0') !==
['authenticate', 'createService', 'now', 'openDatabase']
.sort()
.join('\0') ||
typeof value.openDatabase !== 'function' ||
typeof value.authenticate !== 'function' ||
typeof value.createService !== 'function' ||
typeof value.now !== 'function'
) {
throw new LocalPluginPackageWorkflowCommandConfigurationError(
'runner dependencies are invalid',
);
}
return Object.freeze({ ...value });
}
export async function activateFence(
database: LocalSqlitePluginPackageWorkflowAdministrationDatabase,
authenticated: Readonly<AuthenticatedLocalCommand>,
): Promise<void> {
await authenticated.confirm();
database.activateUserCredentialFence(
authenticated.databaseFence as Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
);
}
@@ -0,0 +1,202 @@
import {
type AuthenticatedLocalCommand,
type LocalPluginPackageWorkflowAdministrationService,
createLocalPluginPackageWorkflowAdministrationService,
establishAuthenticatedLocalCommand,
openLocalSqlitePluginPackageWorkflowAdministrationDatabase,
} from './runnerAuthority';
import {
type LocalPluginPackageWorkflowCommandResult,
type LocalPluginPackageWorkflowCommandRunner,
type LocalPluginPackageWorkflowCommandRunnerDependencies,
} from './contracts';
import { readCommandFile } from './codec';
import { activateFence, dependencies, failureAudit } from './executionSupport';
export function createLocalPluginPackageWorkflowCommandRunner(
candidateDependencies: LocalPluginPackageWorkflowCommandRunnerDependencies = {
openDatabase: openLocalSqlitePluginPackageWorkflowAdministrationDatabase,
authenticate: establishAuthenticatedLocalCommand,
createService: createLocalPluginPackageWorkflowAdministrationService,
now: Date.now,
},
): LocalPluginPackageWorkflowCommandRunner {
const adapters = dependencies(candidateDependencies);
return Object.freeze({
async run(commandFilePath: string) {
const command = readCommandFile(commandFilePath);
const database = await adapters.openDatabase({
databasePath: command.options.databasePath,
profile: command.options.profile,
...(command.options.busyTimeoutMs === undefined
? {}
: { busyTimeoutMs: command.options.busyTimeoutMs }),
});
let authenticated: Readonly<AuthenticatedLocalCommand> | undefined;
try {
try {
authenticated = await adapters.authenticate(database, {
deploymentRoot: command.options.deploymentRoot,
databasePath: command.options.databasePath,
ownerPepperKeyringDirectory:
command.options.ownerPepperKeyringDirectory,
credentialFilePath: command.options.credentialFilePath,
authenticationNamespace: 'local_plugin_package_workflow',
});
await activateFence(database, authenticated);
const service: LocalPluginPackageWorkflowAdministrationService =
adapters.createService(
database.projectPolicy,
database.automationPublications,
database.materializedRevisions,
database.workflowAdministration,
database.securityAudit,
{ now: adapters.now },
);
if (command.operation === 'workflow.inspect') {
const { failureAuditEventId: _failure, ...request } =
command.request;
const result = await service.inspect({
...request,
principal: authenticated.principal,
});
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
projectId: command.request.projectId,
packageName: command.request.packageName,
...result,
});
}
if (command.operation === 'workflow.cancel') {
const { failureAuditEventId: _failure, ...request } =
command.request;
const result = await service.cancel({
...request,
principal: authenticated.principal,
});
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
...result,
});
}
if (command.operation === 'workflow.run.inspect') {
const { failureAuditEventId: _failure, ...request } =
command.request;
const result = await service.inspectRun({
...request,
principal: authenticated.principal,
});
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
projectId: result.projectId,
packageName: result.packageName,
workflowId: result.workflowId,
runId: result.runId,
found: result.found,
run: result.run,
stepCount: result.stepCount,
stepStatusCounts: result.stepStatusCounts,
});
}
if (command.operation === 'workflow.run.list') {
const { failureAuditEventId: _failure, ...request } =
command.request;
const result = await service.listRuns({
...request,
principal: authenticated.principal,
});
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
projectId: result.projectId,
packageName: result.packageName,
workflowId: result.workflowId,
after: result.after,
runs: result.runs,
truncated: result.truncated,
next: result.next,
});
}
if (command.operation === 'workflow.step.list') {
const { failureAuditEventId: _failure, ...request } =
command.request;
const result = await service.listStepRuns({
...request,
principal: authenticated.principal,
});
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
projectId: result.projectId,
packageName: result.packageName,
workflowId: result.workflowId,
runId: result.runId,
found: result.found,
stepRuns: result.stepRuns,
truncated: result.truncated,
next: result.next,
});
}
if (command.operation === 'workflow.event.list') {
const { failureAuditEventId: _failure, ...request } =
command.request;
const result = await service.listRunEvents({
...request,
principal: authenticated.principal,
});
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
projectId: result.projectId,
packageName: result.packageName,
workflowId: result.workflowId,
runId: result.runId,
found: result.found,
afterSequence: result.afterSequence,
headSequence: result.headSequence,
events: result.events,
truncated: result.truncated,
nextAfterSequence: result.nextAfterSequence,
});
}
const { failureAuditEventId: _failure, ...request } = command.request;
const result = await service.start({
...request,
principal: authenticated.principal,
});
return Object.freeze({
schemaVersion: 1 as const,
operation: command.operation,
status: result.status,
projectId: result.plan.target.projectId,
packageName: result.plan.target.packageName,
workflowId: result.plan.target.workflowId,
runId: result.plan.runId,
stepCount: result.plan.steps.length,
admittedAtMs: result.receipt.admittedAtMs,
});
} catch (error) {
const audit = failureAudit(
command,
authenticated,
error,
adapters.now(),
);
if (audit) await database.securityAudit.record(audit);
throw error;
}
} finally {
await database.close();
}
},
});
}
export function runLocalPluginPackageWorkflowCommandFile(
commandFilePath: string,
): Promise<LocalPluginPackageWorkflowCommandResult> {
return createLocalPluginPackageWorkflowCommandRunner().run(commandFilePath);
}
@@ -0,0 +1,5 @@
export { createLocalPluginPackageWorkflowAdministrationService } from '@qinglong/local-admin/plugin-package-workflow-administration';
export type { LocalPluginPackageWorkflowAdministrationService } from '@qinglong/local-admin/plugin-package-workflow-administration';
export { establishAuthenticatedLocalCommand } from '@qinglong/local-owner-console/authenticated-command';
export type { AuthenticatedLocalCommand } from '@qinglong/local-owner-console/authenticated-command';
export { openLocalSqlitePluginPackageWorkflowAdministrationDatabase } from '@qinglong/local-sqlite/plugin-package-workflow-administration';
@@ -0,0 +1,22 @@
export {
LocalPluginPackageWorkflowAdministrationAuthenticationError,
LocalPluginPackageWorkflowAdministrationAuthorizationError,
LocalPluginPackageWorkflowAdministrationConfigurationError,
LocalPluginPackageWorkflowAdministrationNotFoundError,
LocalPluginPackageWorkflowAdministrationUnavailableError,
} from '@qinglong/local-admin/plugin-package-workflow-administration';
export { AuthenticatedLocalCommandAuthenticationError } from '@qinglong/local-owner-console/authenticated-command';
export type { AuthenticatedLocalCommand } from '@qinglong/local-owner-console/authenticated-command';
export { LocalSqliteAuthenticatedManagementFenceError } from '@qinglong/local-sqlite/authenticated-management';
export type { LocalSqliteAuthenticatedUserCredentialFence } from '@qinglong/local-sqlite/authenticated-management';
export type { LocalSqlitePluginPackageWorkflowAdministrationDatabase } from '@qinglong/local-sqlite/plugin-package-workflow-administration';
export {
PluginPackageWorkflowAdministrationAuthorizationFenceConflictError,
PluginPackageWorkflowAdministrationMutationConflictError,
} from '@qinglong/runtime-core/plugin-package-workflow-administration';
export {
InvalidPluginPackageWorkflowExecutionPlanError,
PluginPackageWorkflowAdmissionConflictError,
PluginPackageWorkflowAdmissionNotAllowedError,
} from '@qinglong/runtime-core/plugin-package-workflow-execution-plan';
export type { SecurityAuditRecord } from '@qinglong/runtime-core/security-audit';
@@ -0,0 +1,52 @@
#!/usr/bin/env node
import { runLocalPluginPackageCatalogCommandFile } from './pluginPackageCatalogCommand';
const USAGE =
'Usage: ql3-package-catalog run --command-file /absolute/private-command.json';
async function main(argv: readonly string[]): Promise<void> {
if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h')) {
process.stdout.write(`${USAGE}\n`);
return;
}
const commandFilePath = argv[2];
if (
argv.length !== 3 ||
argv[0] !== 'run' ||
argv[1] !== '--command-file' ||
commandFilePath === undefined
) {
process.stderr.write(
`${JSON.stringify({
code: 'LOCAL_PLUGIN_PACKAGE_CATALOG_CLI_USAGE_INVALID',
message: USAGE,
})}\n`,
);
process.exitCode = 64;
return;
}
try {
const result =
await runLocalPluginPackageCatalogCommandFile(commandFilePath);
process.stdout.write(`${JSON.stringify(result)}\n`);
} catch (error) {
const candidate = error as {
readonly code?: unknown;
readonly name?: unknown;
};
process.stderr.write(
`${JSON.stringify({
code:
typeof candidate.code === 'string'
? candidate.code
: 'LOCAL_PLUGIN_PACKAGE_CATALOG_CLI_FAILED',
name:
typeof candidate.name === 'string' ? candidate.name : 'Error',
})}\n`,
);
process.exitCode = 1;
}
}
void main(process.argv.slice(2));
@@ -0,0 +1,865 @@
// Plugin Package owns recovery catalog publication and inspection commands.
import path from 'node:path';
import {
collectLocalPluginPackageRecoveryCatalog,
createLocalPluginPackagePublisherTrustRegistry,
inspectLocalPluginPackageRecoveryCatalog,
LOCAL_PLUGIN_PACKAGE_PUBLISHER_TRUST_SCHEMA,
publishLocalPluginPackageRecoveryCatalogEntry,
type CollectLocalPluginPackageRecoveryCatalogOptions,
type PublishLocalPluginPackageRecoveryCatalogOptions,
} from '@qinglong/local-admin/package-recovery-catalog';
import { assertLocalPluginPackagePublisherKeyPublicationAllowed } from '@qinglong/local-admin/package-publisher-trust';
import {
PrivateLocalCommandFileError,
readPrivateLocalCommandFile,
readPrivateLocalJsonFile,
} from '@qinglong/local-command-file';
import {
establishAuthenticatedLocalCommand,
type AuthenticatedLocalCommand,
} from '@qinglong/local-owner-console/authenticated-command';
import {
openLocalSqliteAuthenticatedManagementDatabase,
type LocalSqliteAuthenticatedManagementDatabase,
type LocalSqliteProfile,
} from '@qinglong/local-sqlite/authenticated-management';
import { LocalSqlitePluginPackageInstallRepository } from '@qinglong/local-sqlite/plugin-package-install';
export const LOCAL_PLUGIN_PACKAGE_RECOVERY_PUBLICATION_SCHEMA =
'qinglong/local-plugin-package-recovery-publication@v1' as const;
const MAX_PATH_BYTES = 4_096;
const MAX_DESCRIPTOR_BYTES = 256 * 1024;
const MAX_TRUST_BYTES = 256 * 1024;
const UUID_V4_PATTERN =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
const PROJECT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const PACKAGE_NAME_PATTERN =
/^(?:@[a-z0-9][a-z0-9._-]{0,63}\/)?[a-z0-9][a-z0-9._-]{0,127}$/;
const COLLECTION_LIMITS = Object.freeze({
edge: 4,
standalone: 16,
} as const);
export interface LocalPluginPackageCatalogCommandOptions {
readonly deploymentRoot: string;
readonly databasePath: string;
readonly profile: LocalSqliteProfile;
readonly ownerPepperKeyringDirectory: string;
readonly credentialFilePath: string;
readonly catalogRoot: string;
readonly bundleRoot: string;
readonly trustRoot: string;
readonly busyTimeoutMs?: number;
}
interface MutationIdentity {
readonly requestId: string;
readonly auditEventId: string;
readonly failureAuditEventId: string;
}
export interface PublishLocalPluginPackageCatalogCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.catalog.publish';
readonly options: LocalPluginPackageCatalogCommandOptions;
readonly request: MutationIdentity & {
readonly projectId: string;
readonly packageName: string;
readonly descriptorFilePath: string;
};
}
export interface CollectLocalPluginPackageCatalogCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.catalog.collect';
readonly options: LocalPluginPackageCatalogCommandOptions;
readonly request: MutationIdentity & {
readonly limit?: number;
};
}
export interface InspectLocalPluginPackageCatalogCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.catalog.inspect';
readonly options: LocalPluginPackageCatalogCommandOptions;
readonly request: Readonly<Record<never, never>>;
}
export type LocalPluginPackageCatalogCommand =
| PublishLocalPluginPackageCatalogCommand
| CollectLocalPluginPackageCatalogCommand
| InspectLocalPluginPackageCatalogCommand;
export type LocalPluginPackageCatalogCommandResult =
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.catalog.publish';
status: 'published' | 'existing';
lockDigest: string;
artifactDigest: string;
}>
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.catalog.collect';
removedEntries: number;
removedBundles: number;
removedTransactions: number;
remaining: boolean;
}>
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.catalog.inspect';
entryCount: number;
bundleCount: number;
unresolvedTransactions: number;
currentEntries: number;
staleEntries: number;
}>;
export interface LocalPluginPackageCatalogCommandRunner {
run(
commandFilePath: string,
): Promise<Readonly<LocalPluginPackageCatalogCommandResult>>;
}
interface PublicationDescriptor {
readonly schema: typeof LOCAL_PLUGIN_PACKAGE_RECOVERY_PUBLICATION_SCHEMA;
readonly bundlePath: string;
readonly manifest: PublishLocalPluginPackageRecoveryCatalogOptions['manifest'];
readonly signature: PublishLocalPluginPackageRecoveryCatalogOptions['signature'];
}
interface LocalPluginPackageCatalogCommandRunnerDependencies {
readonly openDatabase: typeof openLocalSqliteAuthenticatedManagementDatabase;
readonly authenticate: typeof establishAuthenticatedLocalCommand;
readonly publish: typeof publishLocalPluginPackageRecoveryCatalogEntry;
readonly inspect: typeof inspectLocalPluginPackageRecoveryCatalog;
readonly collect: typeof collectLocalPluginPackageRecoveryCatalog;
readonly now: () => number;
}
export class LocalPluginPackageCatalogCommandConfigurationError extends TypeError {
readonly code = 'LOCAL_PLUGIN_PACKAGE_CATALOG_COMMAND_CONFIGURATION_INVALID';
constructor(message: string, readonly cause?: unknown) {
super(
`Local Plugin Package catalog command configuration is invalid: ${message}`,
);
this.name = 'LocalPluginPackageCatalogCommandConfigurationError';
}
}
export class LocalPluginPackageCatalogCommandConflictError extends Error {
readonly code = 'LOCAL_PLUGIN_PACKAGE_CATALOG_COMMAND_CONFLICT';
constructor(message: string) {
super(
`Local Plugin Package catalog command conflicts with durable state: ${message}`,
);
this.name = 'LocalPluginPackageCatalogCommandConflictError';
}
}
function exactObject(
value: unknown,
expectedKeys: readonly string[],
label: string,
): asserts value is Record<string, unknown> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
(Object.getPrototypeOf(value) !== Object.prototype &&
Object.getPrototypeOf(value) !== null)
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
`${label} must be an object`,
);
}
const descriptors = Object.getOwnPropertyDescriptors(value);
const actual = Object.keys(value).sort();
const expected = [...expectedKeys].sort();
if (
Object.values(descriptors).some(
(descriptor) =>
descriptor.get !== undefined ||
descriptor.set !== undefined ||
descriptor.enumerable !== true,
) ||
actual.length !== expected.length ||
actual.some((key, index) => key !== expected[index])
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
`${label} shape is invalid`,
);
}
}
function boundedPath(value: unknown, label: string): string {
if (
typeof value !== 'string' ||
value.length === 0 ||
Buffer.byteLength(value, 'utf8') > MAX_PATH_BYTES ||
value.includes('\0') ||
!path.isAbsolute(value) ||
path.normalize(value) !== value ||
path.parse(value).root === value
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
`${label} must be a normalized bounded absolute non-root path`,
);
}
return value;
}
function descendant(root: string, candidate: string, label: string): void {
const relative = path.relative(root, candidate);
if (
relative.length === 0 ||
relative === '..' ||
relative.startsWith(`..${path.sep}`) ||
path.isAbsolute(relative)
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
`${label} must be a descendant of deploymentRoot`,
);
}
}
function options(value: unknown): LocalPluginPackageCatalogCommandOptions {
const hasBusyTimeout =
!!value &&
typeof value === 'object' &&
!Array.isArray(value) &&
Object.hasOwn(value, 'busyTimeoutMs');
exactObject(
value,
[
'bundleRoot',
'catalogRoot',
'credentialFilePath',
'databasePath',
'deploymentRoot',
'ownerPepperKeyringDirectory',
'profile',
'trustRoot',
...(hasBusyTimeout ? ['busyTimeoutMs'] : []),
],
'options',
);
const deploymentRoot = boundedPath(value.deploymentRoot, 'deploymentRoot');
const result = {
deploymentRoot,
databasePath: boundedPath(value.databasePath, 'databasePath'),
profile: value.profile,
ownerPepperKeyringDirectory: boundedPath(
value.ownerPepperKeyringDirectory,
'ownerPepperKeyringDirectory',
),
credentialFilePath: boundedPath(
value.credentialFilePath,
'credentialFilePath',
),
catalogRoot: boundedPath(value.catalogRoot, 'catalogRoot'),
bundleRoot: boundedPath(value.bundleRoot, 'bundleRoot'),
trustRoot: boundedPath(value.trustRoot, 'trustRoot'),
...(hasBusyTimeout ? { busyTimeoutMs: value.busyTimeoutMs as number } : {}),
};
if (result.profile !== 'edge' && result.profile !== 'standalone') {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'profile must be edge or standalone',
);
}
if (
hasBusyTimeout &&
(!Number.isSafeInteger(result.busyTimeoutMs) ||
(result.busyTimeoutMs as number) < 100 ||
(result.busyTimeoutMs as number) > 30_000)
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'busyTimeoutMs is invalid',
);
}
const authorityPaths = [
result.databasePath,
result.ownerPepperKeyringDirectory,
result.credentialFilePath,
result.catalogRoot,
result.bundleRoot,
result.trustRoot,
];
for (const [index, authorityPath] of authorityPaths.entries()) {
descendant(deploymentRoot, authorityPath, `authority path ${index}`);
}
if (new Set(authorityPaths).size !== authorityPaths.length) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'authority paths must be distinct',
);
}
return Object.freeze(result as LocalPluginPackageCatalogCommandOptions);
}
function mutationIdentity(
value: Record<string, unknown>,
extraKeys: readonly string[],
label: string,
): asserts value is Record<string, unknown> & MutationIdentity {
exactObject(
value,
['auditEventId', 'failureAuditEventId', 'requestId', ...extraKeys],
label,
);
if (
typeof value.requestId !== 'string' ||
value.requestId.length < 1 ||
value.requestId.length > 128 ||
typeof value.auditEventId !== 'string' ||
!UUID_V4_PATTERN.test(value.auditEventId) ||
typeof value.failureAuditEventId !== 'string' ||
!UUID_V4_PATTERN.test(value.failureAuditEventId) ||
value.auditEventId === value.failureAuditEventId
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
`${label} identity is invalid`,
);
}
}
function normalizeCommand(
value: unknown,
): Readonly<LocalPluginPackageCatalogCommand> {
exactObject(
value,
['operation', 'options', 'request', 'schemaVersion'],
'command',
);
if (value.schemaVersion !== 1) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'schemaVersion is invalid',
);
}
const commandOptions = options(value.options);
if (value.operation === 'plugin-package.catalog.publish') {
exactObject(
value.request,
[
'auditEventId',
'descriptorFilePath',
'failureAuditEventId',
'packageName',
'projectId',
'requestId',
],
'publication request',
);
mutationIdentity(
value.request,
['descriptorFilePath', 'packageName', 'projectId'],
'publication request',
);
if (
typeof value.request.projectId !== 'string' ||
!PROJECT_ID_PATTERN.test(value.request.projectId) ||
typeof value.request.packageName !== 'string' ||
!PACKAGE_NAME_PATTERN.test(value.request.packageName)
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'publication package identity is invalid',
);
}
const descriptorFilePath = boundedPath(
value.request.descriptorFilePath,
'descriptorFilePath',
);
descendant(
commandOptions.deploymentRoot,
descriptorFilePath,
'descriptorFilePath',
);
if (
new Set([
commandOptions.databasePath,
commandOptions.credentialFilePath,
commandOptions.catalogRoot,
commandOptions.bundleRoot,
commandOptions.trustRoot,
descriptorFilePath,
]).size !== 6
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'publication authority paths must be distinct',
);
}
return Object.freeze({
schemaVersion: 1,
operation: value.operation,
options: commandOptions,
request: Object.freeze({
...value.request,
descriptorFilePath,
}),
} as PublishLocalPluginPackageCatalogCommand);
}
if (value.operation === 'plugin-package.catalog.collect') {
const hasLimit =
!!value.request &&
typeof value.request === 'object' &&
!Array.isArray(value.request) &&
Object.hasOwn(value.request, 'limit');
exactObject(
value.request,
[
'auditEventId',
'failureAuditEventId',
'requestId',
...(hasLimit ? ['limit'] : []),
],
'collection request',
);
mutationIdentity(
value.request,
hasLimit ? ['limit'] : [],
'collection request',
);
const limit = value.request.limit;
if (
limit !== undefined &&
(!Number.isSafeInteger(limit) ||
(limit as number) < 1 ||
(limit as number) > COLLECTION_LIMITS[commandOptions.profile])
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'collection limit is invalid',
);
}
return Object.freeze({
schemaVersion: 1,
operation: value.operation,
options: commandOptions,
request: Object.freeze({
requestId: value.request.requestId,
auditEventId: value.request.auditEventId,
failureAuditEventId: value.request.failureAuditEventId,
...(limit === undefined ? {} : { limit: limit as number }),
}),
});
}
if (value.operation === 'plugin-package.catalog.inspect') {
exactObject(value.request, [], 'inspection request');
return Object.freeze({
schemaVersion: 1,
operation: value.operation,
options: commandOptions,
request: Object.freeze({}),
});
}
throw new LocalPluginPackageCatalogCommandConfigurationError(
'operation is invalid',
);
}
function readCommandFile(
commandFilePath: string,
): Readonly<LocalPluginPackageCatalogCommand> {
try {
return normalizeCommand(readPrivateLocalCommandFile(commandFilePath));
} catch (error) {
if (error instanceof LocalPluginPackageCatalogCommandConfigurationError) {
throw error;
}
if (error instanceof PrivateLocalCommandFileError) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'command file cannot be read',
error,
);
}
throw error;
}
}
function publicationDescriptor(
filePath: string,
deploymentRoot: string,
): Readonly<PublicationDescriptor> {
let parsed: unknown;
try {
parsed = readPrivateLocalJsonFile(filePath, {
maxBytes: MAX_DESCRIPTOR_BYTES,
});
} catch (error) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'publication descriptor cannot be read',
error,
);
}
exactObject(
parsed,
['bundlePath', 'manifest', 'schema', 'signature'],
'publication descriptor',
);
if (parsed.schema !== LOCAL_PLUGIN_PACKAGE_RECOVERY_PUBLICATION_SCHEMA) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'publication descriptor schema is invalid',
);
}
const bundlePath = boundedPath(parsed.bundlePath, 'bundlePath');
descendant(deploymentRoot, bundlePath, 'bundlePath');
return Object.freeze({
schema: LOCAL_PLUGIN_PACKAGE_RECOVERY_PUBLICATION_SCHEMA,
bundlePath,
manifest:
parsed.manifest as PublishLocalPluginPackageRecoveryCatalogOptions['manifest'],
signature:
parsed.signature as PublishLocalPluginPackageRecoveryCatalogOptions['signature'],
});
}
function trustFile(
filePath: string,
): ReturnType<typeof createLocalPluginPackagePublisherTrustRegistry> {
let parsed: unknown;
try {
parsed = readPrivateLocalJsonFile(filePath, {
maxBytes: MAX_TRUST_BYTES,
});
} catch (error) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'publisher trust file cannot be read',
error,
);
}
if (
!parsed ||
typeof parsed !== 'object' ||
Array.isArray(parsed) ||
(parsed as { schema?: unknown }).schema !==
LOCAL_PLUGIN_PACKAGE_PUBLISHER_TRUST_SCHEMA
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'publisher trust file schema is invalid',
);
}
return createLocalPluginPackagePublisherTrustRegistry(parsed);
}
async function confirmOwner(
database: LocalSqliteAuthenticatedManagementDatabase,
authenticated: Readonly<AuthenticatedLocalCommand>,
): Promise<void> {
await authenticated.confirm();
database.confirmUserCredentialFence(authenticated.databaseFence);
database.confirmDefaultProjectOwnerFence(authenticated.databaseFence);
}
type SecurityAudit = Parameters<
LocalSqliteAuthenticatedManagementDatabase['securityAudit']['record']
>[0];
function databaseTime(
database: LocalSqliteAuthenticatedManagementDatabase,
eventId: string,
now: () => number,
): number {
const existing = database.authority.client
.prepare(
`SELECT "occurred_at_ms" AS "occurredAtMs"
FROM "QingLong3SecurityAuditEvents"
WHERE "event_id" = ?`,
)
.get(eventId) as { readonly occurredAtMs?: unknown } | undefined;
const value = existing?.occurredAtMs ?? now();
if (!Number.isSafeInteger(value) || (value as number) < 0) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'audit clock is invalid',
);
}
return value as number;
}
async function recordAuthorized(
command:
| Readonly<PublishLocalPluginPackageCatalogCommand>
| Readonly<CollectLocalPluginPackageCatalogCommand>,
database: LocalSqliteAuthenticatedManagementDatabase,
authenticated: Readonly<AuthenticatedLocalCommand>,
projectId: string | null,
now: () => number,
): Promise<void> {
const audit: SecurityAudit = Object.freeze({
eventId: command.request.auditEventId,
requestId: command.request.requestId,
operationId:
command.operation === 'plugin-package.catalog.publish'
? 'plugin_package_catalog_publish'
: 'plugin_package_catalog_collect',
projectId,
subject: authenticated.principal.subject,
authenticationId: authenticated.principal.authenticationId,
outcome: 'allowed',
reasons: Object.freeze(['catalog_mutation_authorized']),
fence: null,
occurredAtMs: databaseTime(database, command.request.auditEventId, now),
});
await database.securityAudit.record(audit);
}
async function staleLockDigests(
repository: LocalSqlitePluginPackageInstallRepository,
lockDigests: readonly string[],
): Promise<readonly string[]> {
const stale: string[] = [];
for (const lockDigest of lockDigests) {
const lock = await repository.findLock(lockDigest);
if (!lock) {
stale.push(lockDigest);
continue;
}
const head = await repository.find(lock.projectId, lock.packageName);
if (!head || head.lockDigest !== lockDigest) stale.push(lockDigest);
}
return Object.freeze(stale);
}
async function execute(
command: Readonly<LocalPluginPackageCatalogCommand>,
database: LocalSqliteAuthenticatedManagementDatabase,
authenticated: Readonly<AuthenticatedLocalCommand>,
adapters: Readonly<LocalPluginPackageCatalogCommandRunnerDependencies>,
): Promise<Readonly<LocalPluginPackageCatalogCommandResult>> {
await confirmOwner(database, authenticated);
const repository = new LocalSqlitePluginPackageInstallRepository(
database.authority,
);
if (command.operation === 'plugin-package.catalog.publish') {
const head = await repository.find(
command.request.projectId,
command.request.packageName,
);
if (!head || head.state === 'failed') {
throw new LocalPluginPackageCatalogCommandConflictError(
'the current install head is unavailable',
);
}
const lock = await repository.findLock(head.lockDigest);
if (
!lock ||
lock.projectId !== command.request.projectId ||
lock.packageName !== command.request.packageName
) {
throw new LocalPluginPackageCatalogCommandConflictError(
'the current PackageLock is unavailable',
);
}
const descriptor = publicationDescriptor(
command.request.descriptorFilePath,
command.options.deploymentRoot,
);
if (
descriptor.bundlePath === command.request.descriptorFilePath ||
descriptor.bundlePath === command.options.catalogRoot ||
descriptor.bundlePath === command.options.bundleRoot ||
descriptor.bundlePath === command.options.trustRoot
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'publication descriptor authorities must be distinct',
);
}
const trust = trustFile(
path.join(command.options.trustRoot, 'current.json'),
);
const result = await adapters.publish({
catalogRoot: command.options.catalogRoot,
bundleRoot: command.options.bundleRoot,
sourceBundlePath: descriptor.bundlePath,
lock,
manifest: descriptor.manifest,
signature: descriptor.signature,
trust,
confirmPublicationAllowed() {
assertLocalPluginPackagePublisherKeyPublicationAllowed({
trustRoot: command.options.trustRoot,
publisher: descriptor.signature.publisher,
keyId: descriptor.signature.keyId,
});
},
async beforePublish() {
await confirmOwner(database, authenticated);
await recordAuthorized(
command,
database,
authenticated,
lock.projectId,
adapters.now,
);
await confirmOwner(database, authenticated);
},
} satisfies PublishLocalPluginPackageRecoveryCatalogOptions);
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
status: result.status,
lockDigest: result.lockDigest,
artifactDigest: result.artifactDigest,
});
}
const inspection = adapters.inspect({
catalogRoot: command.options.catalogRoot,
bundleRoot: command.options.bundleRoot,
});
const stale = await staleLockDigests(repository, inspection.lockDigests);
if (command.operation === 'plugin-package.catalog.inspect') {
await confirmOwner(database, authenticated);
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
entryCount: inspection.entryCount,
bundleCount: inspection.bundleCount,
unresolvedTransactions: inspection.unresolvedTransactions,
currentEntries: inspection.entryCount - stale.length,
staleEntries: stale.length,
});
}
const result = await adapters.collect({
catalogRoot: command.options.catalogRoot,
bundleRoot: command.options.bundleRoot,
candidateLockDigests: stale,
maxDeletes:
command.request.limit ?? COLLECTION_LIMITS[command.options.profile],
async beforeDelete() {
await confirmOwner(database, authenticated);
await recordAuthorized(
command,
database,
authenticated,
null,
adapters.now,
);
await confirmOwner(database, authenticated);
},
} satisfies CollectLocalPluginPackageRecoveryCatalogOptions);
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
...result,
});
}
function failureAudit(
command: Readonly<LocalPluginPackageCatalogCommand>,
database: LocalSqliteAuthenticatedManagementDatabase,
authenticated: Readonly<AuthenticatedLocalCommand> | undefined,
now: () => number,
): Readonly<SecurityAudit> | null {
if (command.operation === 'plugin-package.catalog.inspect') return null;
return Object.freeze({
eventId: command.request.failureAuditEventId,
requestId: command.request.requestId,
operationId:
command.operation === 'plugin-package.catalog.publish'
? 'plugin_package_catalog_publish'
: 'plugin_package_catalog_collect',
projectId:
command.operation === 'plugin-package.catalog.publish'
? command.request.projectId
: null,
subject: authenticated?.principal.subject ?? null,
authenticationId: authenticated?.principal.authenticationId ?? null,
outcome: authenticated
? 'authorization_unavailable'
: 'authentication_rejected',
reasons: Object.freeze([
authenticated ? 'catalog_mutation_failed' : 'credential_rejected',
]),
fence: null,
occurredAtMs: databaseTime(
database,
command.request.failureAuditEventId,
now,
),
});
}
function dependencies(
value: LocalPluginPackageCatalogCommandRunnerDependencies,
): Readonly<LocalPluginPackageCatalogCommandRunnerDependencies> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
Object.keys(value).sort().join('\0') !==
['authenticate', 'collect', 'inspect', 'now', 'openDatabase', 'publish']
.sort()
.join('\0') ||
typeof value.openDatabase !== 'function' ||
typeof value.authenticate !== 'function' ||
typeof value.publish !== 'function' ||
typeof value.inspect !== 'function' ||
typeof value.collect !== 'function' ||
typeof value.now !== 'function'
) {
throw new LocalPluginPackageCatalogCommandConfigurationError(
'runner dependencies are invalid',
);
}
return Object.freeze({ ...value });
}
export function createLocalPluginPackageCatalogCommandRunner(
candidateDependencies: LocalPluginPackageCatalogCommandRunnerDependencies = {
openDatabase: openLocalSqliteAuthenticatedManagementDatabase,
authenticate: establishAuthenticatedLocalCommand,
publish: publishLocalPluginPackageRecoveryCatalogEntry,
inspect: inspectLocalPluginPackageRecoveryCatalog,
collect: collectLocalPluginPackageRecoveryCatalog,
now: Date.now,
},
): LocalPluginPackageCatalogCommandRunner {
const adapters = dependencies(candidateDependencies);
return Object.freeze({
async run(commandFilePath: string) {
const command = readCommandFile(commandFilePath);
const database = await adapters.openDatabase({
databasePath: command.options.databasePath,
profile: command.options.profile,
...(command.options.busyTimeoutMs === undefined
? {}
: { busyTimeoutMs: command.options.busyTimeoutMs }),
});
let authenticated: Readonly<AuthenticatedLocalCommand> | undefined;
try {
try {
authenticated = await adapters.authenticate(database, {
deploymentRoot: command.options.deploymentRoot,
databasePath: command.options.databasePath,
ownerPepperKeyringDirectory:
command.options.ownerPepperKeyringDirectory,
credentialFilePath: command.options.credentialFilePath,
authenticationNamespace: 'local_package_catalog',
});
return await execute(command, database, authenticated, adapters);
} catch (error) {
const audit = failureAudit(
command,
database,
authenticated,
adapters.now,
);
if (audit) await database.securityAudit.record(audit);
throw error;
}
} finally {
await database.close();
}
},
});
}
export function runLocalPluginPackageCatalogCommandFile(
commandFilePath: string,
): Promise<Readonly<LocalPluginPackageCatalogCommandResult>> {
return createLocalPluginPackageCatalogCommandRunner().run(commandFilePath);
}
@@ -0,0 +1,55 @@
#!/usr/bin/env node
import { runLocalPluginPackageCommandFile } from './pluginPackageCommand';
const USAGE =
'Usage: ql3-package run --command-file /absolute/private-command.json';
async function main(argv: readonly string[]): Promise<void> {
if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h')) {
process.stdout.write(`${USAGE}\n`);
return;
}
const commandFilePath = argv[2];
if (
argv.length !== 3 ||
argv[0] !== 'run' ||
argv[1] !== '--command-file' ||
commandFilePath === undefined
) {
process.stderr.write(
`${JSON.stringify({
code: 'LOCAL_PLUGIN_PACKAGE_CLI_USAGE_INVALID',
message: USAGE,
})}\n`,
);
process.exitCode = 64;
return;
}
try {
const result = await runLocalPluginPackageCommandFile(commandFilePath);
process.stdout.write(`${JSON.stringify(result)}\n`);
} catch (error) {
const candidate = error as {
readonly code?: unknown;
readonly name?: unknown;
readonly message?: unknown;
};
process.stderr.write(
`${JSON.stringify({
code:
typeof candidate.code === 'string'
? candidate.code
: 'LOCAL_PLUGIN_PACKAGE_CLI_FAILED',
name: typeof candidate.name === 'string' ? candidate.name : 'Error',
message:
typeof candidate.message === 'string'
? candidate.message
: 'Local Plugin Package command failed',
})}\n`,
);
process.exitCode = 1;
}
}
void main(process.argv.slice(2));
@@ -0,0 +1,982 @@
// Plugin Package owns its authenticated lifecycle command surface.
import { randomUUID } from 'node:crypto';
import path from 'node:path';
import {
PrivateLocalCommandFileError,
readPrivateLocalCommandFile,
} from '@qinglong/local-command-file';
import {
establishAuthenticatedLocalCommand,
type AuthenticatedLocalCommand,
} from '@qinglong/local-owner-console/authenticated-command';
import {
openLocalSqlitePluginPackageManagementDatabase,
type LocalSqlitePluginPackageManagementDatabase,
type LocalSqliteProfile,
} from '@qinglong/local-sqlite/package-management';
import type {
ApprovalRequestRecord,
ApprovedActionDispatchRecord,
} from '@qinglong/runtime-core/approved-action';
import type { ApprovedActionDispatchBatchSummary } from '@qinglong/runtime-core/approved-action-dispatcher';
import {
normalizePluginPackageLifecycleImpact,
type PluginPackageLifecycleAction,
type PluginPackageLifecycleImpact,
type PluginPackageLifecycleReceipt,
} from '@qinglong/runtime-core/plugin-package-lifecycle';
import {
pluginPackageInstallRecoveryAction,
type PluginPackageInstallActionInput,
type PluginPackageInstallInventoryItem,
} from '@qinglong/runtime-core/plugin-package-install';
import type { PluginPackageInstallProposal } from '@qinglong/runtime-core/plugin-package-proposal';
import { createLocalPluginPackageManagementService } from '@qinglong/local-admin/package-management';
import { createLocalPluginPackageLifecycleService } from '@qinglong/local-admin/package-lifecycle';
const MAX_PATH_BYTES = 4096;
const MAX_DISPATCH_LIMIT = 64;
const LOCAL_INSTALLATION_INVENTORY_LIMITS = Object.freeze({
edge: 16,
standalone: 64,
});
const LOCAL_INSTALLATION_INVENTORY_DEFAULTS = Object.freeze({
edge: 8,
standalone: 32,
});
const PROJECT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const PACKAGE_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
const COMMAND_IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
const REASON_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
const LOCAL_PACKAGE_CONSUMER = Object.freeze({
subject: Object.freeze({
type: 'system' as const,
id: 'local_plugin_package_consumer',
}),
authenticationId: 'local_plugin_package_consumer_v1',
});
export interface LocalPluginPackageCommandOptions {
readonly deploymentRoot: string;
readonly databasePath: string;
readonly profile: LocalSqliteProfile;
readonly ownerPepperKeyringDirectory: string;
readonly credentialFilePath: string;
readonly busyTimeoutMs?: number;
}
export interface ProposeLocalPluginPackageCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.propose';
readonly options: LocalPluginPackageCommandOptions;
readonly request: {
readonly actionRef: string;
readonly approvalRequestId: string;
readonly proposalAuditEventId: string;
readonly approvalAuditEventId: string;
readonly actionInput: PluginPackageInstallActionInput;
};
}
export interface DecideLocalPluginPackageCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.decide';
readonly options: LocalPluginPackageCommandOptions;
readonly request: {
readonly actionRef: string;
readonly approvalRequestId: string;
readonly expectedVersion: number;
readonly decisionId: string;
readonly auditEventId: string;
readonly decision: 'approved' | 'rejected';
readonly reasonCode: string;
};
}
export interface ConsumeLocalPluginPackageCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.consume';
readonly options: LocalPluginPackageCommandOptions;
readonly request: {
readonly actionRef: string;
readonly approvalRequestId: string;
readonly expectedVersion: number;
readonly consumptionId: string;
readonly dispatchId: string;
readonly auditEventId: string;
};
}
export interface InspectLocalPluginPackageCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.inspect';
readonly options: LocalPluginPackageCommandOptions;
readonly request: {
readonly actionRef: string;
readonly approvalRequestId: string;
};
}
export interface InspectLocalPluginPackageInstallationCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.installation.inspect';
readonly options: LocalPluginPackageCommandOptions;
readonly request: {
readonly projectId: string;
readonly packageName: string;
};
}
export interface ListLocalPluginPackageInstallationsCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.installation.list';
readonly options: LocalPluginPackageCommandOptions;
readonly request: {
readonly projectId: string;
readonly limit?: number;
readonly after?: Readonly<{ packageName: string }>;
};
}
export interface DispatchLocalPluginPackageCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.dispatch';
readonly options: LocalPluginPackageCommandOptions;
readonly request: {
readonly limit?: number;
};
}
export interface PlanLocalPluginPackageLifecycleCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.lifecycle.plan';
readonly options: LocalPluginPackageCommandOptions;
readonly request: {
readonly action: PluginPackageLifecycleAction;
readonly projectId: string;
readonly packageName: string;
};
}
export interface ExecuteLocalPluginPackageLifecycleCommand {
readonly schemaVersion: 1;
readonly operation: 'plugin-package.lifecycle.execute';
readonly options: LocalPluginPackageCommandOptions;
readonly request: {
readonly impact: PluginPackageLifecycleImpact;
readonly approvalRequestId: string;
readonly decisionId: string;
readonly consumptionId: string;
readonly dispatchId: string;
readonly approvalAuditEventId: string;
readonly decisionAuditEventId: string;
readonly consumptionAuditEventId: string;
readonly reasonCode: string;
};
}
export type LocalPluginPackageCommand =
| ProposeLocalPluginPackageCommand
| DecideLocalPluginPackageCommand
| ConsumeLocalPluginPackageCommand
| InspectLocalPluginPackageCommand
| InspectLocalPluginPackageInstallationCommand
| ListLocalPluginPackageInstallationsCommand
| DispatchLocalPluginPackageCommand
| PlanLocalPluginPackageLifecycleCommand
| ExecuteLocalPluginPackageLifecycleCommand;
export interface LocalPluginPackageCommandRunner {
run(
commandFilePath: string,
): Promise<Readonly<LocalPluginPackageCommandResult>>;
}
export type LocalPluginPackageCommandResult =
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.propose';
proposalStatus: 'created' | 'existing';
approvalStatus: 'created' | 'existing';
proposal: ReturnType<typeof proposalSummary>;
approval: ReturnType<typeof approvalSummary>;
}>
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.decide';
status: 'decided' | 'existing';
approval: ReturnType<typeof approvalSummary>;
}>
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.consume';
status: 'consumed' | 'existing';
approval: ReturnType<typeof approvalSummary>;
dispatch: ReturnType<typeof dispatchSummary>;
}>
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.inspect';
proposal: ReturnType<typeof proposalSummary> | null;
approval: ReturnType<typeof approvalSummary> | null;
}>
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.installation.inspect';
installation: ReturnType<typeof installationSummary> | null;
}>
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.installation.list';
installations: readonly ReturnType<typeof installationSummary>[];
truncated: boolean;
next: Readonly<{ packageName: string }> | null;
}>
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.dispatch';
summary: Readonly<ApprovedActionDispatchBatchSummary>;
}>
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.lifecycle.plan';
impact: Readonly<PluginPackageLifecycleImpact>;
summary: ReturnType<typeof lifecycleImpactSummary>;
}>
| Readonly<{
schemaVersion: 1;
operation: 'plugin-package.lifecycle.execute';
status: 'created' | 'existing';
approval: ReturnType<typeof approvalSummary>;
receipt: ReturnType<typeof lifecycleReceiptSummary>;
}>;
export class LocalPluginPackageCommandConfigurationError extends TypeError {
readonly code = 'LOCAL_PLUGIN_PACKAGE_COMMAND_CONFIGURATION_INVALID';
constructor(message: string, readonly cause?: unknown) {
super(`Local Plugin Package command configuration is invalid: ${message}`);
this.name = 'LocalPluginPackageCommandConfigurationError';
}
}
interface LocalPluginPackageCommandRunnerDependencies {
readonly openDatabase: typeof openLocalSqlitePluginPackageManagementDatabase;
readonly authenticate: typeof establishAuthenticatedLocalCommand;
}
function exactObject(
value: unknown,
expectedKeys: readonly string[],
label: string,
): asserts value is Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new LocalPluginPackageCommandConfigurationError(
`${label} must be an object`,
);
}
const actual = Object.keys(value).sort();
const expected = [...expectedKeys].sort();
if (
actual.length !== expected.length ||
actual.some((key, index) => key !== expected[index])
) {
throw new LocalPluginPackageCommandConfigurationError(
`${label} shape is invalid`,
);
}
}
function boundedPath(value: unknown, label: string): string {
if (
typeof value !== 'string' ||
!path.isAbsolute(value) ||
path.parse(value).root === value ||
path.normalize(value) !== value ||
value.includes('\0') ||
Buffer.byteLength(value, 'utf8') > MAX_PATH_BYTES
) {
throw new LocalPluginPackageCommandConfigurationError(
`${label} must be a normalized bounded absolute non-root path`,
);
}
return value;
}
function options(value: unknown): LocalPluginPackageCommandOptions {
const hasBusyTimeout =
!!value &&
typeof value === 'object' &&
!Array.isArray(value) &&
Object.hasOwn(value, 'busyTimeoutMs');
exactObject(
value,
[
'deploymentRoot',
'databasePath',
'profile',
'ownerPepperKeyringDirectory',
'credentialFilePath',
...(hasBusyTimeout ? ['busyTimeoutMs'] : []),
],
'options',
);
for (const key of [
'deploymentRoot',
'databasePath',
'ownerPepperKeyringDirectory',
'credentialFilePath',
] as const) {
boundedPath(value[key], key);
}
if (value.profile !== 'edge' && value.profile !== 'standalone') {
throw new LocalPluginPackageCommandConfigurationError(
'profile must be edge or standalone',
);
}
if (
value.busyTimeoutMs !== undefined &&
(!Number.isSafeInteger(value.busyTimeoutMs) ||
(value.busyTimeoutMs as number) < 100 ||
(value.busyTimeoutMs as number) > 30_000)
) {
throw new LocalPluginPackageCommandConfigurationError(
'busyTimeoutMs is invalid',
);
}
return Object.freeze(value as unknown as LocalPluginPackageCommandOptions);
}
function normalizeCommand(value: unknown): Readonly<LocalPluginPackageCommand> {
exactObject(
value,
['schemaVersion', 'operation', 'options', 'request'],
'command',
);
if (value.schemaVersion !== 1) {
throw new LocalPluginPackageCommandConfigurationError(
'schemaVersion is invalid',
);
}
const commandOptions = options(value.options);
switch (value.operation) {
case 'plugin-package.propose':
exactObject(
value.request,
[
'actionRef',
'approvalRequestId',
'proposalAuditEventId',
'approvalAuditEventId',
'actionInput',
],
'proposal request',
);
break;
case 'plugin-package.decide':
exactObject(
value.request,
[
'actionRef',
'approvalRequestId',
'expectedVersion',
'decisionId',
'auditEventId',
'decision',
'reasonCode',
],
'decision request',
);
break;
case 'plugin-package.consume':
exactObject(
value.request,
[
'actionRef',
'approvalRequestId',
'expectedVersion',
'consumptionId',
'dispatchId',
'auditEventId',
],
'consumption request',
);
break;
case 'plugin-package.inspect':
exactObject(
value.request,
['actionRef', 'approvalRequestId'],
'inspection request',
);
break;
case 'plugin-package.installation.inspect':
exactObject(
value.request,
['packageName', 'projectId'],
'installation inspection request',
);
if (
typeof value.request.projectId !== 'string' ||
!PROJECT_ID_PATTERN.test(value.request.projectId) ||
typeof value.request.packageName !== 'string' ||
!PACKAGE_NAME_PATTERN.test(value.request.packageName)
) {
throw new LocalPluginPackageCommandConfigurationError(
'installation inspection identity is invalid',
);
}
break;
case 'plugin-package.installation.list': {
const request =
value.request && typeof value.request === 'object'
? (value.request as Record<string, unknown>)
: {};
const hasLimit = Object.hasOwn(request, 'limit');
const hasAfter = Object.hasOwn(request, 'after');
exactObject(
value.request,
[
'projectId',
...(hasLimit ? ['limit'] : []),
...(hasAfter ? ['after'] : []),
],
'installation list request',
);
if (
typeof request.projectId !== 'string' ||
!PROJECT_ID_PATTERN.test(request.projectId) ||
(request.limit !== undefined &&
(!Number.isSafeInteger(request.limit) ||
(request.limit as number) < 1 ||
(request.limit as number) >
LOCAL_INSTALLATION_INVENTORY_LIMITS[commandOptions.profile]))
) {
throw new LocalPluginPackageCommandConfigurationError(
'installation list request is invalid',
);
}
if (request.after !== undefined) {
exactObject(request.after, ['packageName'], 'installation list cursor');
if (
typeof request.after.packageName !== 'string' ||
!PACKAGE_NAME_PATTERN.test(request.after.packageName)
) {
throw new LocalPluginPackageCommandConfigurationError(
'installation list cursor is invalid',
);
}
}
break;
}
case 'plugin-package.dispatch': {
const hasLimit =
!!value.request &&
typeof value.request === 'object' &&
!Array.isArray(value.request) &&
Object.hasOwn(value.request, 'limit');
exactObject(value.request, hasLimit ? ['limit'] : [], 'dispatch request');
if (
(value.request as { limit?: unknown }).limit !== undefined &&
(!Number.isSafeInteger((value.request as { limit?: unknown }).limit) ||
((value.request as { limit?: number }).limit as number) < 1 ||
((value.request as { limit?: number }).limit as number) >
MAX_DISPATCH_LIMIT)
) {
throw new LocalPluginPackageCommandConfigurationError(
'dispatch limit is invalid',
);
}
break;
}
case 'plugin-package.lifecycle.plan':
exactObject(
value.request,
['action', 'packageName', 'projectId'],
'lifecycle plan request',
);
if (
(value.request.action !== 'disable' &&
value.request.action !== 'enable' &&
value.request.action !== 'uninstall') ||
typeof value.request.projectId !== 'string' ||
!PROJECT_ID_PATTERN.test(value.request.projectId) ||
typeof value.request.packageName !== 'string' ||
!PACKAGE_NAME_PATTERN.test(value.request.packageName)
) {
throw new LocalPluginPackageCommandConfigurationError(
'lifecycle plan request is invalid',
);
}
break;
case 'plugin-package.lifecycle.execute':
exactObject(
value.request,
[
'approvalAuditEventId',
'approvalRequestId',
'consumptionAuditEventId',
'consumptionId',
'decisionAuditEventId',
'decisionId',
'dispatchId',
'impact',
'reasonCode',
],
'lifecycle execution request',
);
try {
normalizePluginPackageLifecycleImpact(
value.request.impact as PluginPackageLifecycleImpact,
);
} catch (error) {
throw new LocalPluginPackageCommandConfigurationError(
'lifecycle impact is invalid',
error,
);
}
if (
[
value.request.approvalRequestId,
value.request.decisionId,
value.request.consumptionId,
value.request.dispatchId,
value.request.approvalAuditEventId,
value.request.decisionAuditEventId,
value.request.consumptionAuditEventId,
].some(
(candidate) =>
typeof candidate !== 'string' ||
!COMMAND_IDENTIFIER_PATTERN.test(candidate),
) ||
typeof value.request.reasonCode !== 'string' ||
!REASON_CODE_PATTERN.test(value.request.reasonCode)
) {
throw new LocalPluginPackageCommandConfigurationError(
'lifecycle execution identity is invalid',
);
}
break;
default:
throw new LocalPluginPackageCommandConfigurationError(
'operation is invalid',
);
}
return Object.freeze({
...value,
options: commandOptions,
} as unknown as LocalPluginPackageCommand);
}
function readCommandFile(
commandFilePath: string,
): Readonly<LocalPluginPackageCommand> {
try {
return normalizeCommand(readPrivateLocalCommandFile(commandFilePath));
} catch (error) {
if (error instanceof LocalPluginPackageCommandConfigurationError) {
throw error;
}
if (error instanceof PrivateLocalCommandFileError) {
throw new LocalPluginPackageCommandConfigurationError(
'command file cannot be read',
error,
);
}
throw error;
}
}
function proposalSummary(proposal: Readonly<PluginPackageInstallProposal>) {
return Object.freeze({
actionRef: proposal.actionRef,
projectId: proposal.projectId,
packageName: proposal.actionInput.manifest.metadata.name,
packageVersion: proposal.actionInput.manifest.metadata.version,
operation: proposal.actionInput.plan.operation,
sourceKind: proposal.actionInput.source.kind,
architecture: proposal.actionInput.architecture,
deploymentProfile: proposal.actionInput.deploymentProfile,
targetGeneration: proposal.actionInput.targetGeneration,
actionDigest: proposal.actionDigest,
previewDigest: proposal.previewDigest,
proposalDigest: proposal.proposalDigest,
createdAtMs: proposal.createdAtMs,
});
}
function approvalSummary(approval: Readonly<ApprovalRequestRecord>) {
return Object.freeze({
id: approval.id,
projectId: approval.projectId,
version: approval.version,
state: approval.state,
risk: approval.risk,
decisionMode: approval.decisionMode,
requestedAtMs: approval.requestedAtMs,
expiresAtMs: approval.expiresAtMs,
decision: approval.decision,
decisionReasonCode: approval.decisionReasonCode,
decidedAtMs: approval.decidedAtMs,
dispatchId: approval.dispatchId,
consumedAtMs: approval.consumedAtMs,
actionDigest: approval.action.actionDigest,
previewDigest: approval.action.previewDigest,
});
}
function dispatchSummary(dispatch: Readonly<ApprovedActionDispatchRecord>) {
return Object.freeze({
id: dispatch.id,
approvalRequestId: dispatch.approvalRequestId,
projectId: dispatch.projectId,
actionRef: dispatch.action.actionRef,
actionDigest: dispatch.action.actionDigest,
createdAtMs: dispatch.createdAtMs,
expiresAtMs: dispatch.expiresAtMs,
});
}
function installationSummary(
item: Readonly<PluginPackageInstallInventoryItem>,
) {
const { record, quarantine } = item;
return Object.freeze({
installationId: record.installationId,
projectId: record.projectId,
packageName: record.packageName,
packageVersion: record.packageVersion,
operation: record.operation,
state: record.state,
targetGeneration: record.targetGeneration,
activeLockDigest: record.activeLockDigest,
previousActiveLockDigest: record.previousActiveLockDigest,
recoveryAction: pluginPackageInstallRecoveryAction(record),
availability: quarantine
? ('quarantined' as const)
: record.state === 'active'
? ('active' as const)
: ('not_active' as const),
quarantineReason: quarantine?.reasonCode ?? null,
quarantineAuthorizationMode: quarantine?.authorizationMode ?? null,
quarantineEventDigest: quarantine?.eventDigest ?? null,
quarantinedAtMs: quarantine?.occurredAtMs ?? null,
withdrawalStatus: quarantine?.capabilityStatus ?? null,
withdrawalReceiptDigest: quarantine?.receiptDigest ?? null,
withdrawalCommittedAtMs: quarantine?.committedAtMs ?? null,
failureReason: record.failure?.reason ?? null,
failedFrom: record.failure?.failedFrom ?? null,
failedAtMs: record.failure?.failedAtMs ?? null,
version: record.version,
createdAtMs: record.createdAtMs,
updatedAtMs: record.updatedAtMs,
recordDigest: record.recordDigest,
});
}
function lifecycleImpactSummary(
impact: Readonly<PluginPackageLifecycleImpact>,
) {
return Object.freeze({
action: impact.action,
projectId: impact.target.projectId,
packageName: impact.target.packageName,
installationId: impact.target.installationId,
lockDigest: impact.target.lockDigest,
installVersion: impact.target.installVersion,
installRecordDigest: impact.target.installRecordDigest,
expectedVersion: impact.expected.version,
expectedDisposition: impact.expected.disposition,
expectedEventDigest: impact.expected.eventDigest,
generationDigest: impact.generationDigest,
materializedRevisionDigest: impact.materializedRevisionDigest,
currentToolSnapshotDigest: impact.currentToolSnapshotDigest,
taskIds: impact.taskIds,
resourceCounts: impact.resourceCounts,
referenceGraphDigest: impact.referenceGraphDigest,
blockingReferences: impact.blockingReferences,
impactDigest: impact.impactDigest,
});
}
function lifecycleReceiptSummary(
receipt: Readonly<PluginPackageLifecycleReceipt>,
) {
return Object.freeze({
eventDigest: receipt.eventDigest,
action: receipt.action,
projectId: receipt.target.projectId,
packageName: receipt.target.packageName,
installationId: receipt.target.installationId,
lockDigest: receipt.target.lockDigest,
lifecycleVersion: receipt.lifecycle.version,
disposition: receipt.lifecycle.disposition,
capabilityStatus: receipt.capability.status,
taskTransitions: receipt.capability.taskTransitions.length,
previousActiveVectorDigest:
receipt.capability.previousActiveVectorDigest,
currentActiveVectorDigest: receipt.capability.currentActiveVectorDigest,
currentToolSnapshotDigest: receipt.capability.currentToolSnapshotDigest,
retainedSourceCount: receipt.capability.retainedSourceCount,
committedAtMs: receipt.committedAtMs,
receiptDigest: receipt.receiptDigest,
});
}
async function execute(
command: Readonly<LocalPluginPackageCommand>,
database: LocalSqlitePluginPackageManagementDatabase,
authenticated: Readonly<AuthenticatedLocalCommand>,
): Promise<Readonly<LocalPluginPackageCommandResult>> {
await authenticated.confirm();
if (command.operation === 'plugin-package.lifecycle.plan') {
const service = createLocalPluginPackageLifecycleService({
authority: database.authority,
});
const impact = await service.plan(
command.request.action,
command.request.projectId,
command.request.packageName,
authenticated.principal,
);
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
impact,
summary: lifecycleImpactSummary(impact),
});
}
if (command.operation === 'plugin-package.lifecycle.execute') {
const service = createLocalPluginPackageLifecycleService({
authority: database.authority,
});
const result = await service.execute({
...command.request,
principal: authenticated.principal,
confirmAuthorization: authenticated.confirm,
});
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
status: result.status,
approval: approvalSummary(result.approval),
receipt: lifecycleReceiptSummary(result.receipt),
});
}
if (command.operation === 'plugin-package.installation.inspect') {
const { LocalSqlitePluginPackageInstallRepository } = await import(
'@qinglong/local-sqlite/plugin-package-install'
);
const repository = new LocalSqlitePluginPackageInstallRepository(
database.authority,
);
const item = await repository.findCurrent(
command.request.projectId,
command.request.packageName,
);
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
installation: item ? installationSummary(item) : null,
});
}
if (command.operation === 'plugin-package.installation.list') {
const { LocalSqlitePluginPackageInstallRepository } = await import(
'@qinglong/local-sqlite/plugin-package-install'
);
const repository = new LocalSqlitePluginPackageInstallRepository(
database.authority,
);
const page = await repository.listCurrentPage({
projectId: command.request.projectId,
limit:
command.request.limit ??
LOCAL_INSTALLATION_INVENTORY_DEFAULTS[command.options.profile],
...(command.request.after === undefined
? {}
: { after: command.request.after }),
});
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
installations: Object.freeze(page.items.map(installationSummary)),
truncated: page.truncated,
next: page.next ?? null,
});
}
const service = createLocalPluginPackageManagementService({
authority: database.authority,
profile: command.options.profile,
consumer: LOCAL_PACKAGE_CONSUMER,
dispatcher: {
owner: 'local_plugin_package_dispatcher',
defaultBatchSize: 4,
createId: randomUUID,
},
});
switch (command.operation) {
case 'plugin-package.propose': {
const current = await service.inspect(
command.request.actionRef,
command.request.approvalRequestId,
);
const result = await service.propose({
...command.request,
requestedAtMs:
current.approvalRequest?.requestedAtMs ??
current.proposal?.createdAtMs ??
Date.now(),
principal: authenticated.principal,
});
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
proposalStatus: result.proposalStatus,
approvalStatus: result.approvalStatus,
proposal: proposalSummary(result.proposal),
approval: approvalSummary(result.approvalRequest),
});
}
case 'plugin-package.decide': {
const { actionRef, ...decisionRequest } = command.request;
const current = await service.inspect(
actionRef,
command.request.approvalRequestId,
);
if (
current.approvalRequest?.action.actionRef === actionRef &&
current.approvalRequest.decisionId === command.request.decisionId &&
current.approvalRequest.decision === command.request.decision &&
current.approvalRequest.decisionReasonCode ===
command.request.reasonCode &&
current.approvalRequest.decidedBy?.type ===
authenticated.principal.subject.type &&
current.approvalRequest.decidedBy.id ===
authenticated.principal.subject.id
) {
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
status: 'existing',
approval: approvalSummary(current.approvalRequest),
});
}
const result = await service.decide({
...decisionRequest,
decidedAtMs: Date.now(),
principal: authenticated.principal,
});
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
status: result.status,
approval: approvalSummary(result.request),
});
}
case 'plugin-package.consume': {
const { actionRef, ...consumptionRequest } = command.request;
const current = await service.inspect(
actionRef,
command.request.approvalRequestId,
);
const result = await service.consume({
...consumptionRequest,
consumedAtMs:
current.approvalRequest?.consumptionId ===
command.request.consumptionId &&
current.approvalRequest.dispatchId === command.request.dispatchId &&
current.approvalRequest.consumedAtMs !== null
? current.approvalRequest.consumedAtMs
: Date.now(),
});
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
status: result.status,
approval: approvalSummary(result.request),
dispatch: dispatchSummary(result.dispatch),
});
}
case 'plugin-package.inspect': {
const result = await service.inspect(
command.request.actionRef,
command.request.approvalRequestId,
);
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
proposal: result.proposal ? proposalSummary(result.proposal) : null,
approval: result.approvalRequest
? approvalSummary(result.approvalRequest)
: null,
});
}
case 'plugin-package.dispatch': {
const result = await service.dispatch(command.request.limit);
return Object.freeze({
schemaVersion: 1,
operation: command.operation,
summary: result,
});
}
}
}
function dependencies(
value: LocalPluginPackageCommandRunnerDependencies,
): Readonly<LocalPluginPackageCommandRunnerDependencies> {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
Object.keys(value).length !== 2 ||
typeof value.openDatabase !== 'function' ||
typeof value.authenticate !== 'function'
) {
throw new LocalPluginPackageCommandConfigurationError(
'runner dependencies are invalid',
);
}
return Object.freeze({ ...value });
}
export function createLocalPluginPackageCommandRunner(
candidateDependencies: LocalPluginPackageCommandRunnerDependencies = {
openDatabase: openLocalSqlitePluginPackageManagementDatabase,
authenticate: establishAuthenticatedLocalCommand,
},
): LocalPluginPackageCommandRunner {
const adapters = dependencies(candidateDependencies);
return Object.freeze({
async run(commandFilePath: string) {
const command = readCommandFile(commandFilePath);
const database = await adapters.openDatabase({
databasePath: command.options.databasePath,
profile: command.options.profile,
...(command.options.busyTimeoutMs === undefined
? {}
: { busyTimeoutMs: command.options.busyTimeoutMs }),
});
try {
const authenticated = await adapters.authenticate(database, {
deploymentRoot: command.options.deploymentRoot,
databasePath: command.options.databasePath,
ownerPepperKeyringDirectory:
command.options.ownerPepperKeyringDirectory,
credentialFilePath: command.options.credentialFilePath,
authenticationNamespace: 'local_package',
});
return await execute(command, database, authenticated);
} finally {
await database.close();
}
},
});
}
export function runLocalPluginPackageCommandFile(
commandFilePath: string,
): Promise<Readonly<LocalPluginPackageCommandResult>> {
return createLocalPluginPackageCommandRunner().run(commandFilePath);
}
@@ -0,0 +1,52 @@
#!/usr/bin/env node
import { runLocalPluginPackagePromptCommandFile } from './pluginPackagePromptCommand';
const USAGE =
'Usage: ql3-prompt run --command-file /absolute/private-command.json';
async function main(argv: readonly string[]): Promise<void> {
if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h')) {
process.stdout.write(`${USAGE}\n`);
return;
}
const commandFilePath = argv[2];
if (
argv.length !== 3 ||
argv[0] !== 'run' ||
argv[1] !== '--command-file' ||
commandFilePath === undefined
) {
process.stderr.write(
`${JSON.stringify({
code: 'LOCAL_PLUGIN_PACKAGE_PROMPT_CLI_USAGE_INVALID',
message: USAGE,
})}\n`,
);
process.exitCode = 64;
return;
}
try {
const result = await runLocalPluginPackagePromptCommandFile(
commandFilePath,
);
process.stdout.write(`${JSON.stringify(result)}\n`);
} catch (error) {
const candidate = error as {
readonly code?: unknown;
readonly name?: unknown;
};
process.stderr.write(
`${JSON.stringify({
code:
typeof candidate.code === 'string'
? candidate.code
: 'LOCAL_PLUGIN_PACKAGE_PROMPT_CLI_FAILED',
name: typeof candidate.name === 'string' ? candidate.name : 'Error',
})}\n`,
);
process.exitCode = 1;
}
}
void main(process.argv.slice(2));
@@ -0,0 +1,26 @@
// Plugin Package owns server-derived Prompt execution commands.
export {
LocalPluginPackagePromptAuthenticationError,
LocalPluginPackagePromptAuthorizationError,
LocalPluginPackagePromptCommandConfigurationError,
LocalPluginPackagePromptNotFoundError,
LocalPluginPackagePromptUnavailableError,
} from './plugin-package-prompt-command/contracts';
export type {
ExecuteLocalPluginPackagePromptCommand,
InspectLocalPluginPackagePromptCommand,
InspectLocalPluginPackagePromptExecutionCommand,
LocalPluginPackagePromptCommand,
LocalPluginPackagePromptCommandOptions,
LocalPluginPackagePromptCommandResult,
LocalPluginPackagePromptCommandRunner,
LocalPluginPackagePromptCommandRunnerDependencies,
LocalPluginPackagePromptInspectCommandOptions,
LocalPluginPackagePromptOutputCommandOptions,
LocalPluginPackagePromptOutputIntent,
ReadLocalPluginPackagePromptExecutionOutputCommand,
} from './plugin-package-prompt-command/contracts';
export {
createLocalPluginPackagePromptCommandRunner,
runLocalPluginPackagePromptCommandFile,
} from './plugin-package-prompt-command/runner';
@@ -0,0 +1,52 @@
#!/usr/bin/env node
import { runLocalPluginPackagePublisherTrustCommandFile } from './pluginPackagePublisherTrustCommand';
const USAGE =
'Usage: ql3-package-trust run --command-file /absolute/private-command.json';
async function main(argv: readonly string[]): Promise<void> {
if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h')) {
process.stdout.write(`${USAGE}\n`);
return;
}
const commandFilePath = argv[2];
if (
argv.length !== 3 ||
argv[0] !== 'run' ||
argv[1] !== '--command-file' ||
commandFilePath === undefined
) {
process.stderr.write(
`${JSON.stringify({
code: 'LOCAL_PLUGIN_PACKAGE_PUBLISHER_TRUST_CLI_USAGE_INVALID',
message: USAGE,
})}\n`,
);
process.exitCode = 64;
return;
}
try {
const result =
await runLocalPluginPackagePublisherTrustCommandFile(commandFilePath);
process.stdout.write(`${JSON.stringify(result)}\n`);
} catch (error) {
const candidate = error as {
readonly code?: unknown;
readonly name?: unknown;
};
process.stderr.write(
`${JSON.stringify({
code:
typeof candidate.code === 'string'
? candidate.code
: 'LOCAL_PLUGIN_PACKAGE_PUBLISHER_TRUST_CLI_FAILED',
name:
typeof candidate.name === 'string' ? candidate.name : 'Error',
})}\n`,
);
process.exitCode = 1;
}
}
void main(process.argv.slice(2));
@@ -0,0 +1,52 @@
#!/usr/bin/env node
import { runLocalPluginPackageWorkflowCommandFile } from './pluginPackageWorkflowCommand';
const USAGE =
'Usage: ql3-workflow run --command-file /absolute/private-command.json';
async function main(argv: readonly string[]): Promise<void> {
if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h')) {
process.stdout.write(`${USAGE}\n`);
return;
}
const commandFilePath = argv[2];
if (
argv.length !== 3 ||
argv[0] !== 'run' ||
argv[1] !== '--command-file' ||
commandFilePath === undefined
) {
process.stderr.write(
`${JSON.stringify({
code: 'LOCAL_PLUGIN_PACKAGE_WORKFLOW_CLI_USAGE_INVALID',
message: USAGE,
})}\n`,
);
process.exitCode = 64;
return;
}
try {
const result = await runLocalPluginPackageWorkflowCommandFile(
commandFilePath,
);
process.stdout.write(`${JSON.stringify(result)}\n`);
} catch (error) {
const candidate = error as {
readonly code?: unknown;
readonly name?: unknown;
};
process.stderr.write(
`${JSON.stringify({
code:
typeof candidate.code === 'string'
? candidate.code
: 'LOCAL_PLUGIN_PACKAGE_WORKFLOW_CLI_FAILED',
name: typeof candidate.name === 'string' ? candidate.name : 'Error',
})}\n`,
);
process.exitCode = 1;
}
}
void main(process.argv.slice(2));
@@ -0,0 +1,20 @@
// Plugin Package owns generation-bound Workflow administration commands.
export { LocalPluginPackageWorkflowCommandConfigurationError } from './plugin-package-workflow-command/contracts';
export type {
CancelLocalPluginPackageWorkflowCommand,
InspectLocalPluginPackageWorkflowCommand,
InspectLocalPluginPackageWorkflowRunCommand,
ListLocalPluginPackageWorkflowRunEventsCommand,
ListLocalPluginPackageWorkflowRunsCommand,
ListLocalPluginPackageWorkflowStepRunsCommand,
LocalPluginPackageWorkflowCommand,
LocalPluginPackageWorkflowCommandOptions,
LocalPluginPackageWorkflowCommandResult,
LocalPluginPackageWorkflowCommandRunner,
LocalPluginPackageWorkflowCommandRunnerDependencies,
StartLocalPluginPackageWorkflowCommand,
} from './plugin-package-workflow-command/contracts';
export {
createLocalPluginPackageWorkflowCommandRunner,
runLocalPluginPackageWorkflowCommandFile,
} from './plugin-package-workflow-command/runner';