@konitif/workbench 0.285.0 → 0.285.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/application/launch/catalogShareReference.d.ts +25 -0
- package/dist/application/launch/catalogShareReference.js +53 -0
- package/dist/application/launch/workbenchLaunchPolicy.d.ts +100 -0
- package/dist/application/launch/workbenchLaunchPolicy.js +20 -0
- package/dist/application/shell/state.d.ts +1 -0
- package/dist/application/shell/state.js +10 -0
- package/dist/application/tool/experienceToolSelection.d.ts +15 -0
- package/dist/application/tool/experienceToolSelection.js +59 -0
- package/dist/application/widgets/createWorkbenchWidgetAdapter.d.ts +2 -1
- package/dist/application/widgets/createWorkbenchWidgetAdapter.js +3 -0
- package/dist/application/workspace/commands.d.ts +3 -0
- package/dist/application/workspace/commands.js +3 -0
- package/dist/application/workspace/parkedTool.d.ts +22 -0
- package/dist/application/workspace/parkedTool.js +87 -0
- package/dist/application/workspace/presetSelection.d.ts +36 -0
- package/dist/application/workspace/presetSelection.js +44 -0
- package/dist/application/workspace/unassignTool.d.ts +2 -0
- package/dist/application/workspace/unassignTool.js +24 -0
- package/dist/application/workspace/usageSnapshots.d.ts +58 -0
- package/dist/application/workspace/usageSnapshots.js +103 -0
- package/dist/application/workspace/workspacePresetCatalogAdmission.d.ts +32 -0
- package/dist/application/workspace/workspacePresetCatalogAdmission.js +58 -0
- package/dist/domain/catalog/presentation.d.ts +41 -0
- package/dist/domain/catalog/presentation.js +1 -0
- package/dist/domain/catalog/version.d.ts +7 -0
- package/dist/domain/catalog/version.js +1 -0
- package/dist/domain/shell/model.d.ts +6 -0
- package/dist/domain/tool/model.d.ts +6 -0
- package/dist/hosting.d.ts +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/infrastructure/tools/InMemoryToolRegistry.d.ts +1 -0
- package/dist/infrastructure/tools/InMemoryToolRegistry.js +11 -0
- package/dist/workspace-contracts.d.ts +18 -1
- package/dist/workspace-contracts.js +15 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ npm install @konitif/workbench
|
|
|
16
16
|
- Runtime lifecycle, checkpoint, recovery and observability contracts.
|
|
17
17
|
- Project preflight and repository-quality contracts.
|
|
18
18
|
- Explicit workspace persistence, synchronization and surface ports.
|
|
19
|
+
- Content-addressed workspace usage bundles with strict host-context admission.
|
|
19
20
|
|
|
20
21
|
## Authority boundary
|
|
21
22
|
|
|
@@ -25,6 +26,11 @@ Widgets and tools retain their own definitions; Workbench admits and hosts them.
|
|
|
25
26
|
Physical implementations remain in `@konitif/physics` and are only exposed here
|
|
26
27
|
through a compatibility entry.
|
|
27
28
|
|
|
29
|
+
Workspace presets remain authored source. A usage bundle records an immutable,
|
|
30
|
+
content-addressed projection of a preset and its workspace, shell and focus
|
|
31
|
+
fragments. Admission rejects altered content, incomplete fragments, incompatible
|
|
32
|
+
host contexts and identity/revision conflicts.
|
|
33
|
+
|
|
28
34
|
## Quick start
|
|
29
35
|
|
|
30
36
|
```ts
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface WorkbenchCatalogShareReference {
|
|
2
|
+
schemaVersion: 1;
|
|
3
|
+
kind: 'workbench.catalog-reference';
|
|
4
|
+
element: {
|
|
5
|
+
kind: 'tool' | 'widget' | 'resource';
|
|
6
|
+
id: string;
|
|
7
|
+
};
|
|
8
|
+
package: {
|
|
9
|
+
name: string;
|
|
10
|
+
version: string;
|
|
11
|
+
};
|
|
12
|
+
source: {
|
|
13
|
+
status: 'publisher-page' | 'reference-only';
|
|
14
|
+
url?: string;
|
|
15
|
+
};
|
|
16
|
+
privateSource: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function serializeWorkbenchCatalogShareReference(reference: WorkbenchCatalogShareReference): string;
|
|
19
|
+
export declare function parseWorkbenchCatalogShareReference(source: string): {
|
|
20
|
+
ok: true;
|
|
21
|
+
reference: WorkbenchCatalogShareReference;
|
|
22
|
+
} | {
|
|
23
|
+
ok: false;
|
|
24
|
+
message: string;
|
|
25
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export function serializeWorkbenchCatalogShareReference(reference) {
|
|
2
|
+
const validated = validateReference(reference);
|
|
3
|
+
if (!validated.ok)
|
|
4
|
+
throw new Error(validated.message);
|
|
5
|
+
return JSON.stringify(reference);
|
|
6
|
+
}
|
|
7
|
+
export function parseWorkbenchCatalogShareReference(source) {
|
|
8
|
+
try {
|
|
9
|
+
return validateReference(JSON.parse(source));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return { ok: false, message: 'Catalog share reference is not valid JSON.' };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function validateReference(value) {
|
|
16
|
+
const candidate = value;
|
|
17
|
+
if (!candidate || candidate.schemaVersion !== 1 || candidate.kind !== 'workbench.catalog-reference') {
|
|
18
|
+
return { ok: false, message: 'Catalog share reference schema is unsupported.' };
|
|
19
|
+
}
|
|
20
|
+
if (!candidate.element || !isNonEmpty(candidate.element.id) || !['tool', 'widget', 'resource'].includes(candidate.element.kind)) {
|
|
21
|
+
return { ok: false, message: 'Catalog share reference element is invalid.' };
|
|
22
|
+
}
|
|
23
|
+
if (!candidate.package || !isNonEmpty(candidate.package.name) || !isNonEmpty(candidate.package.version)) {
|
|
24
|
+
return { ok: false, message: 'Catalog share reference package identity or version is missing.' };
|
|
25
|
+
}
|
|
26
|
+
if (!candidate.source || !['publisher-page', 'reference-only'].includes(candidate.source.status)) {
|
|
27
|
+
return { ok: false, message: 'Catalog share reference origin is missing.' };
|
|
28
|
+
}
|
|
29
|
+
if (candidate.source.status === 'publisher-page') {
|
|
30
|
+
if (!candidate.source.url || !isSafePublisherUrl(candidate.source.url)) {
|
|
31
|
+
return { ok: false, message: 'Catalog publisher page must be an HTTPS URL without embedded credentials.' };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else if (candidate.source.url !== undefined) {
|
|
35
|
+
return { ok: false, message: 'Reference-only catalog shares cannot claim a publisher URL.' };
|
|
36
|
+
}
|
|
37
|
+
if (typeof candidate.privateSource !== 'boolean') {
|
|
38
|
+
return { ok: false, message: 'Catalog share reference privacy is missing.' };
|
|
39
|
+
}
|
|
40
|
+
return { ok: true, reference: candidate };
|
|
41
|
+
}
|
|
42
|
+
function isSafePublisherUrl(value) {
|
|
43
|
+
try {
|
|
44
|
+
const url = new URL(value);
|
|
45
|
+
return url.protocol === 'https:' && !url.username && !url.password;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function isNonEmpty(value) {
|
|
52
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
53
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export type WorkbenchLaunchMode = 'direct' | 'dashboard';
|
|
2
|
+
export type WorkbenchLaunchModeResolution = {
|
|
3
|
+
ok: true;
|
|
4
|
+
mode: WorkbenchLaunchMode;
|
|
5
|
+
} | {
|
|
6
|
+
ok: false;
|
|
7
|
+
code: 'workbench-launch.invalid-mode';
|
|
8
|
+
message: string;
|
|
9
|
+
};
|
|
10
|
+
export interface WorkbenchDashboardPreview {
|
|
11
|
+
kind: 'layout' | 'image';
|
|
12
|
+
label: string;
|
|
13
|
+
src?: string;
|
|
14
|
+
alt?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface WorkbenchDashboardEntry<Selection = unknown> {
|
|
17
|
+
id: string;
|
|
18
|
+
title: string;
|
|
19
|
+
eyebrow: string;
|
|
20
|
+
description: string;
|
|
21
|
+
actionLabel: string;
|
|
22
|
+
selection: Selection;
|
|
23
|
+
detail?: string;
|
|
24
|
+
preview?: WorkbenchDashboardPreview;
|
|
25
|
+
}
|
|
26
|
+
export interface WorkbenchLibrarySourceDescriptor {
|
|
27
|
+
id: string;
|
|
28
|
+
label: string;
|
|
29
|
+
kind: 'local' | 'remote';
|
|
30
|
+
status: 'available' | 'not-configured';
|
|
31
|
+
}
|
|
32
|
+
export interface WorkbenchLibraryItem {
|
|
33
|
+
id: string;
|
|
34
|
+
sourceId: string;
|
|
35
|
+
kind: 'tool' | 'widget' | 'resource';
|
|
36
|
+
title: string;
|
|
37
|
+
description: string;
|
|
38
|
+
versions?: {
|
|
39
|
+
local?: string | null;
|
|
40
|
+
localProvenance?: string | null;
|
|
41
|
+
published?: {
|
|
42
|
+
status: 'verified' | 'not-configured' | 'unavailable';
|
|
43
|
+
version?: string | null;
|
|
44
|
+
channel?: 'npm' | 'github' | 'repository' | 'other';
|
|
45
|
+
publisher?: string | null;
|
|
46
|
+
sourceUrl?: string | null;
|
|
47
|
+
reason?: string | null;
|
|
48
|
+
acquisition: {
|
|
49
|
+
status: 'available' | 'unavailable';
|
|
50
|
+
reason?: string | null;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
hostApplication?: {
|
|
54
|
+
label: string;
|
|
55
|
+
version: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
icon?: {
|
|
59
|
+
src: string;
|
|
60
|
+
alt: string;
|
|
61
|
+
};
|
|
62
|
+
illustration?: {
|
|
63
|
+
src: string;
|
|
64
|
+
alt: string;
|
|
65
|
+
};
|
|
66
|
+
metadata?: Readonly<Record<string, string>>;
|
|
67
|
+
details?: {
|
|
68
|
+
description?: string;
|
|
69
|
+
dependencies?: readonly import("../../domain/catalog/presentation.js").WorkbenchCatalogDependency[];
|
|
70
|
+
diagram?: import("../../domain/catalog/presentation.js").WorkbenchCatalogDiagram;
|
|
71
|
+
};
|
|
72
|
+
share?: {
|
|
73
|
+
kind: 'catalog-reference';
|
|
74
|
+
reference: string;
|
|
75
|
+
publisherUrl?: string | null;
|
|
76
|
+
limitation: string;
|
|
77
|
+
privateSource: boolean;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export interface WorkbenchLaunchAdmissionIssue {
|
|
81
|
+
id: string;
|
|
82
|
+
title: string;
|
|
83
|
+
description: string;
|
|
84
|
+
requiredVersion?: string;
|
|
85
|
+
availableVersions?: string[];
|
|
86
|
+
acquisition: {
|
|
87
|
+
status: 'available' | 'unavailable';
|
|
88
|
+
actionLabel?: string;
|
|
89
|
+
reason?: string;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export interface WorkbenchLaunchAdmissionBlocker {
|
|
93
|
+
title: string;
|
|
94
|
+
description: string;
|
|
95
|
+
issues: WorkbenchLaunchAdmissionIssue[];
|
|
96
|
+
}
|
|
97
|
+
export declare function resolveWorkbenchLaunchMode(input: {
|
|
98
|
+
requestedMode?: string | readonly string[] | null;
|
|
99
|
+
configuredDefault?: WorkbenchLaunchMode;
|
|
100
|
+
}): WorkbenchLaunchModeResolution;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function resolveWorkbenchLaunchMode(input) {
|
|
2
|
+
const requestedMode = input.requestedMode;
|
|
3
|
+
if (requestedMode === null || requestedMode === undefined) {
|
|
4
|
+
return { ok: true, mode: input.configuredDefault ?? 'direct' };
|
|
5
|
+
}
|
|
6
|
+
if (typeof requestedMode !== 'string' || requestedMode !== requestedMode.trim() || requestedMode.length === 0) {
|
|
7
|
+
return invalidMode();
|
|
8
|
+
}
|
|
9
|
+
if (requestedMode !== 'direct' && requestedMode !== 'dashboard') {
|
|
10
|
+
return invalidMode();
|
|
11
|
+
}
|
|
12
|
+
return { ok: true, mode: requestedMode };
|
|
13
|
+
}
|
|
14
|
+
function invalidMode() {
|
|
15
|
+
return {
|
|
16
|
+
ok: false,
|
|
17
|
+
code: 'workbench-launch.invalid-mode',
|
|
18
|
+
message: 'Workbench launch mode must be either "direct" or "dashboard".'
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -7,6 +7,7 @@ export declare function resolveShellRegionAxis(region: ShellRegionState): ShellR
|
|
|
7
7
|
export declare function isShellRegionWidgetVisible(region: ShellRegionState, widgetId: string): boolean;
|
|
8
8
|
export declare function listVisibleShellRegionWidgetIds(region: ShellRegionState): string[];
|
|
9
9
|
export declare function setShellRegionArrangement(shellState: ShellState, regionId: ShellRegionId, presentation: ShellRegionPresentation, axis?: ShellRegionAxis): ShellState;
|
|
10
|
+
export declare function setShellRegionWidgetProportions(shellState: ShellState, regionId: ShellRegionId, proportions: Record<string, number>): ShellState;
|
|
10
11
|
export declare function createShellState(widgetCatalog: ShellWidgetCatalogPort, options?: {
|
|
11
12
|
openRegionIds?: ShellRegionId[];
|
|
12
13
|
}): ShellState;
|
|
@@ -38,6 +38,16 @@ export function setShellRegionArrangement(shellState, regionId, presentation, ax
|
|
|
38
38
|
axis: nextAxis
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
+
export function setShellRegionWidgetProportions(shellState, regionId, proportions) {
|
|
42
|
+
const region = shellState.regions[regionId];
|
|
43
|
+
if (!region)
|
|
44
|
+
return shellState;
|
|
45
|
+
const widgetProportions = Object.fromEntries(region.widgetIds.flatMap(id => {
|
|
46
|
+
const value = proportions[id];
|
|
47
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? [[id, value]] : [];
|
|
48
|
+
}));
|
|
49
|
+
return updateShellRegion(shellState, regionId, { ...region, widgetProportions });
|
|
50
|
+
}
|
|
41
51
|
export function createShellState(widgetCatalog, options = {}) {
|
|
42
52
|
return {
|
|
43
53
|
regions: {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { InMemoryToolRegistry } from "../../infrastructure/tools/InMemoryToolRegistry.js";
|
|
2
|
+
import type { ToolDefinition } from "../../domain/tool/model.js";
|
|
3
|
+
export declare class WorkbenchExperienceToolSelection {
|
|
4
|
+
private readonly availableRegistry;
|
|
5
|
+
readonly registry: InMemoryToolRegistry;
|
|
6
|
+
constructor(availableRegistry: InMemoryToolRegistry, initialToolIds?: readonly string[]);
|
|
7
|
+
listAvailable(): ToolDefinition[];
|
|
8
|
+
listSelected(): ToolDefinition[];
|
|
9
|
+
isSelected(toolId: string): boolean;
|
|
10
|
+
select(toolId: string): boolean;
|
|
11
|
+
selectAll(toolIds: readonly string[]): boolean;
|
|
12
|
+
replaceWith(toolIds: readonly string[]): boolean;
|
|
13
|
+
deselect(toolId: string): boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function createWorkbenchExperienceToolSelection(availableRegistry: InMemoryToolRegistry, initialToolIds?: readonly string[]): WorkbenchExperienceToolSelection;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { InMemoryToolRegistry } from "../../infrastructure/tools/InMemoryToolRegistry.js";
|
|
2
|
+
export class WorkbenchExperienceToolSelection {
|
|
3
|
+
availableRegistry;
|
|
4
|
+
registry = new InMemoryToolRegistry();
|
|
5
|
+
constructor(availableRegistry, initialToolIds = []) {
|
|
6
|
+
this.availableRegistry = availableRegistry;
|
|
7
|
+
for (const toolId of initialToolIds)
|
|
8
|
+
this.select(toolId);
|
|
9
|
+
}
|
|
10
|
+
listAvailable() {
|
|
11
|
+
return this.availableRegistry.list().map(entry => entry.definition);
|
|
12
|
+
}
|
|
13
|
+
listSelected() {
|
|
14
|
+
return this.registry.list().map(entry => entry.definition);
|
|
15
|
+
}
|
|
16
|
+
isSelected(toolId) {
|
|
17
|
+
return Boolean(this.registry.get(toolId));
|
|
18
|
+
}
|
|
19
|
+
select(toolId) {
|
|
20
|
+
if (this.registry.get(toolId))
|
|
21
|
+
return true;
|
|
22
|
+
const entry = this.availableRegistry.get(toolId);
|
|
23
|
+
if (!entry)
|
|
24
|
+
return false;
|
|
25
|
+
this.registry.register(cloneRegistration(entry));
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
selectAll(toolIds) {
|
|
29
|
+
const uniqueToolIds = [...new Set(toolIds)];
|
|
30
|
+
if (uniqueToolIds.some(toolId => !this.registry.get(toolId) && !this.availableRegistry.get(toolId))) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
for (const toolId of uniqueToolIds)
|
|
34
|
+
this.select(toolId);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
replaceWith(toolIds) {
|
|
38
|
+
const uniqueToolIds = [...new Set(toolIds)];
|
|
39
|
+
if (uniqueToolIds.some(toolId => !this.availableRegistry.get(toolId)))
|
|
40
|
+
return false;
|
|
41
|
+
const requested = new Set(uniqueToolIds);
|
|
42
|
+
for (const tool of this.listSelected()) {
|
|
43
|
+
if (!requested.has(tool.id))
|
|
44
|
+
this.registry.unregister(tool.id);
|
|
45
|
+
}
|
|
46
|
+
for (const toolId of uniqueToolIds)
|
|
47
|
+
this.select(toolId);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
deselect(toolId) {
|
|
51
|
+
return this.registry.unregister(toolId);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function createWorkbenchExperienceToolSelection(availableRegistry, initialToolIds = []) {
|
|
55
|
+
return new WorkbenchExperienceToolSelection(availableRegistry, initialToolIds);
|
|
56
|
+
}
|
|
57
|
+
function cloneRegistration(entry) {
|
|
58
|
+
return { ...entry };
|
|
59
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { WorkbenchWidgetDefinition } from "../../domain/widget/model.js";
|
|
2
2
|
import type { RegisteredWorkbenchWidget, RegisteredWorkbenchWidgetComponentLoader } from "../../infrastructure/widgets/InMemoryWorkbenchWidgetRegistry.js";
|
|
3
3
|
import { type WidgetDefinitionCatalog } from '@konitif/widgets';
|
|
4
|
+
export type WorkbenchWidgetDefinitionCatalogPort = Pick<WidgetDefinitionCatalog, 'getDefinition'>;
|
|
4
5
|
export type WorkbenchWidgetHosting = Pick<WorkbenchWidgetDefinition, 'icon' | 'supportedSurfaces' | 'presentation'>;
|
|
5
6
|
/** Candidate bridge; neither registers nor loads, and owns no placement state. */
|
|
6
|
-
export declare function createWorkbenchWidgetAdapter(catalog:
|
|
7
|
+
export declare function createWorkbenchWidgetAdapter(catalog: WorkbenchWidgetDefinitionCatalogPort, widgetId: string, load: RegisteredWorkbenchWidgetComponentLoader): Readonly<{
|
|
7
8
|
source: import("@konitif/widgets").WidgetDefinition;
|
|
8
9
|
project(host?: WorkbenchWidgetHosting): RegisteredWorkbenchWidget;
|
|
9
10
|
}>;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { bindWidgetImplementation } from '@konitif/widgets';
|
|
2
2
|
/** Candidate bridge; neither registers nor loads, and owns no placement state. */
|
|
3
3
|
export function createWorkbenchWidgetAdapter(catalog, widgetId, load) {
|
|
4
|
+
// Widgets currently gives its catalog nominal identity through a private Map,
|
|
5
|
+
// while this bridge only needs the public lookup capability. Keep the
|
|
6
|
+
// cross-version boundary structural and contain the nominal cast here.
|
|
4
7
|
const binding = bindWidgetImplementation(catalog, widgetId, load);
|
|
5
8
|
return Object.freeze({
|
|
6
9
|
source: binding.definition,
|
|
@@ -14,6 +14,7 @@ import { swapPanelArea } from "./swapPanelArea.js";
|
|
|
14
14
|
import { createPanel } from "../../domain/workspace/factories.js";
|
|
15
15
|
import { updateActiveWindowRoot, updateStackById } from "./layoutTree.js";
|
|
16
16
|
import { createWorkspaceSessionState, getFocusedPanel } from "./session.js";
|
|
17
|
+
import { unassignTool } from "./unassignTool.js";
|
|
17
18
|
/**
|
|
18
19
|
* Command handling owns focus transitions so Svelte components only emit user intent.
|
|
19
20
|
*/
|
|
@@ -41,6 +42,8 @@ export function dispatchWorkspaceCommand(state, command, context) {
|
|
|
41
42
|
return openToolInPanel(normalizedState, command.panelId, command.toolId, context.toolCatalog);
|
|
42
43
|
case 'open-tool-in-new-tab':
|
|
43
44
|
return openToolInNewTab(normalizedState, command.panelId, command.toolId, context.toolCatalog);
|
|
45
|
+
case 'unassign-tool':
|
|
46
|
+
return createWorkspaceSessionState(unassignTool(normalizedState.workspace, command.toolId), normalizedState.focus);
|
|
44
47
|
case 'run-tool-shell-command':
|
|
45
48
|
return runFocusedToolShellCommand(normalizedState, command, context.toolCatalog);
|
|
46
49
|
case 'split-panel-horizontal':
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ToolInstance } from "../../domain/tool/model.js";
|
|
2
|
+
import type { WorkspaceSessionState } from "../../domain/workspace/session.js";
|
|
3
|
+
export interface ParkedWorkspaceTool {
|
|
4
|
+
schemaVersion: 1;
|
|
5
|
+
toolId: string;
|
|
6
|
+
instances: Record<string, ToolInstance>;
|
|
7
|
+
panelBindings: Array<{
|
|
8
|
+
panelId: string;
|
|
9
|
+
title: string;
|
|
10
|
+
toolInstanceId: string;
|
|
11
|
+
}>;
|
|
12
|
+
activePanelId: string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare function parkWorkspaceTool(session: WorkspaceSessionState, toolId: string): {
|
|
15
|
+
session: WorkspaceSessionState;
|
|
16
|
+
parked: ParkedWorkspaceTool | null;
|
|
17
|
+
};
|
|
18
|
+
export declare function restoreParkedWorkspaceTool(session: WorkspaceSessionState, parked: ParkedWorkspaceTool): {
|
|
19
|
+
session: WorkspaceSessionState;
|
|
20
|
+
restored: boolean;
|
|
21
|
+
};
|
|
22
|
+
export declare function isParkedWorkspaceTool(value: unknown): value is ParkedWorkspaceTool;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { findPanelInWorkspace, listPanelsInWorkspace } from "../../domain/workspace/selectors.js";
|
|
2
|
+
import { createWorkspaceSessionState } from "./session.js";
|
|
3
|
+
import { updatePanelById } from "./layoutTree.js";
|
|
4
|
+
import { unassignTool } from "./unassignTool.js";
|
|
5
|
+
export function parkWorkspaceTool(session, toolId) {
|
|
6
|
+
const instances = Object.fromEntries(Object.entries(session.workspace.toolInstances)
|
|
7
|
+
.filter(([, instance]) => instance.toolId === toolId)
|
|
8
|
+
.map(([instanceId, instance]) => [instanceId, structuredClone(instance)]));
|
|
9
|
+
const instanceIds = new Set(Object.keys(instances));
|
|
10
|
+
if (instanceIds.size === 0)
|
|
11
|
+
return { session, parked: null };
|
|
12
|
+
const panelBindings = listPanelsInWorkspace(session.workspace)
|
|
13
|
+
.filter(panel => panel.toolInstanceId !== null && instanceIds.has(panel.toolInstanceId))
|
|
14
|
+
.map(panel => ({ panelId: panel.id, title: panel.title, toolInstanceId: panel.toolInstanceId }));
|
|
15
|
+
const workspace = unassignTool(session.workspace, toolId);
|
|
16
|
+
return {
|
|
17
|
+
session: createWorkspaceSessionState(workspace, { activePanelId: session.focus.activePanelId }),
|
|
18
|
+
parked: {
|
|
19
|
+
schemaVersion: 1,
|
|
20
|
+
toolId,
|
|
21
|
+
instances,
|
|
22
|
+
panelBindings,
|
|
23
|
+
activePanelId: instanceIds.has(session.focus.activeToolInstanceId ?? '')
|
|
24
|
+
? session.focus.activePanelId
|
|
25
|
+
: null
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function restoreParkedWorkspaceTool(session, parked) {
|
|
30
|
+
if (!isParkedWorkspaceTool(parked))
|
|
31
|
+
return { session, restored: false };
|
|
32
|
+
const instanceIds = new Set(Object.keys(parked.instances));
|
|
33
|
+
const panelsAreAvailable = parked.panelBindings.every(binding => {
|
|
34
|
+
const panel = findPanelInWorkspace(session.workspace, binding.panelId);
|
|
35
|
+
return panel && (panel.toolInstanceId === null || panel.toolInstanceId === binding.toolInstanceId);
|
|
36
|
+
});
|
|
37
|
+
const instancesAreAvailable = [...instanceIds].every(instanceId => {
|
|
38
|
+
const current = session.workspace.toolInstances[instanceId];
|
|
39
|
+
return !current;
|
|
40
|
+
});
|
|
41
|
+
if (!panelsAreAvailable || !instancesAreAvailable)
|
|
42
|
+
return { session, restored: false };
|
|
43
|
+
let windows = session.workspace.windows;
|
|
44
|
+
for (const binding of parked.panelBindings) {
|
|
45
|
+
windows = windows.map(window => ({
|
|
46
|
+
...window,
|
|
47
|
+
root: updatePanelById(window.root, binding.panelId, panel => ({
|
|
48
|
+
...panel,
|
|
49
|
+
title: binding.title,
|
|
50
|
+
toolInstanceId: binding.toolInstanceId
|
|
51
|
+
}))
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
const workspace = {
|
|
55
|
+
...session.workspace,
|
|
56
|
+
windows,
|
|
57
|
+
toolInstances: {
|
|
58
|
+
...session.workspace.toolInstances,
|
|
59
|
+
...structuredClone(parked.instances)
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
session: createWorkspaceSessionState(workspace, {
|
|
64
|
+
activePanelId: parked.activePanelId ?? session.focus.activePanelId
|
|
65
|
+
}),
|
|
66
|
+
restored: true
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export function isParkedWorkspaceTool(value) {
|
|
70
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
71
|
+
return false;
|
|
72
|
+
const candidate = value;
|
|
73
|
+
if (candidate.schemaVersion !== 1 || typeof candidate.toolId !== 'string' || !candidate.toolId)
|
|
74
|
+
return false;
|
|
75
|
+
if (!candidate.instances || typeof candidate.instances !== 'object' || Array.isArray(candidate.instances))
|
|
76
|
+
return false;
|
|
77
|
+
if (!Array.isArray(candidate.panelBindings))
|
|
78
|
+
return false;
|
|
79
|
+
const instances = candidate.instances;
|
|
80
|
+
if (Object.entries(instances).some(([id, instance]) => !instance || instance.id !== id || instance.toolId !== candidate.toolId ||
|
|
81
|
+
!instance.state || typeof instance.state !== 'object' || Array.isArray(instance.state)))
|
|
82
|
+
return false;
|
|
83
|
+
if (candidate.panelBindings.some(binding => !binding || typeof binding.panelId !== 'string' || typeof binding.title !== 'string' ||
|
|
84
|
+
typeof binding.toolInstanceId !== 'string' || !instances[binding.toolInstanceId]))
|
|
85
|
+
return false;
|
|
86
|
+
return candidate.activePanelId === null || typeof candidate.activePanelId === 'string';
|
|
87
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ShellWidgetCatalogPort } from "../../domain/shell/model.js";
|
|
2
|
+
import type { ToolCatalogPort } from "../../domain/tool/model.js";
|
|
3
|
+
import { type WorkspacePresetArtifact, type WorkspacePresetCompatibilityRequest, type WorkspacePresetDiagnostic } from "./presetArtifacts.js";
|
|
4
|
+
export interface WorkspacePresetSelectionRequest<Origin extends string = string> {
|
|
5
|
+
kind: 'workspace-preset-source';
|
|
6
|
+
origin: Origin;
|
|
7
|
+
presetId: string;
|
|
8
|
+
expectedVersion: string | null;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkspacePresetSelectionCandidates {
|
|
11
|
+
sources: readonly WorkspacePresetArtifact[];
|
|
12
|
+
toolCatalog: ToolCatalogPort;
|
|
13
|
+
shellWidgetCatalog: ShellWidgetCatalogPort;
|
|
14
|
+
}
|
|
15
|
+
export interface ResolvedWorkspacePresetSelection<Origin extends string = string> {
|
|
16
|
+
kind: 'workspace-preset-source';
|
|
17
|
+
request: WorkspacePresetSelectionRequest<Origin>;
|
|
18
|
+
source: WorkspacePresetArtifact;
|
|
19
|
+
toolCatalog: ToolCatalogPort;
|
|
20
|
+
shellWidgetCatalog: ShellWidgetCatalogPort;
|
|
21
|
+
}
|
|
22
|
+
export type WorkspacePresetSelectionFailureCode = 'workspace-preset-selection.invalid-request' | 'workspace-preset-selection.unknown' | 'workspace-preset-selection.ambiguous' | 'workspace-preset-selection.version-incompatible' | 'workspace-preset-selection.incompatible';
|
|
23
|
+
export type WorkspacePresetSelectionResult<Origin extends string = string> = {
|
|
24
|
+
ok: true;
|
|
25
|
+
selection: ResolvedWorkspacePresetSelection<Origin>;
|
|
26
|
+
} | {
|
|
27
|
+
ok: false;
|
|
28
|
+
code: WorkspacePresetSelectionFailureCode;
|
|
29
|
+
message: string;
|
|
30
|
+
diagnostics: WorkspacePresetDiagnostic[];
|
|
31
|
+
};
|
|
32
|
+
export declare function resolveWorkspacePresetSelection<Origin extends string>(input: {
|
|
33
|
+
request: WorkspacePresetSelectionRequest<Origin>;
|
|
34
|
+
candidates: WorkspacePresetSelectionCandidates;
|
|
35
|
+
compatibilityRequest?: WorkspacePresetCompatibilityRequest;
|
|
36
|
+
}): WorkspacePresetSelectionResult<Origin>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { evaluateWorkspacePresetCompatibility } from "./presetArtifacts.js";
|
|
2
|
+
export function resolveWorkspacePresetSelection(input) {
|
|
3
|
+
const { request, candidates } = input;
|
|
4
|
+
if (!request.presetId || request.presetId.trim() !== request.presetId) {
|
|
5
|
+
return failure('workspace-preset-selection.invalid-request', 'Workspace preset selection requires a normalized preset id.');
|
|
6
|
+
}
|
|
7
|
+
if (request.expectedVersion !== null &&
|
|
8
|
+
(!request.expectedVersion || request.expectedVersion.trim() !== request.expectedVersion)) {
|
|
9
|
+
return failure('workspace-preset-selection.invalid-request', 'Workspace preset selection contains an invalid expected version.');
|
|
10
|
+
}
|
|
11
|
+
const matchingSources = candidates.sources.filter(candidate => candidate.id === request.presetId);
|
|
12
|
+
if (matchingSources.length === 0) {
|
|
13
|
+
return failure('workspace-preset-selection.unknown', `Workspace preset "${request.presetId}" is unknown.`);
|
|
14
|
+
}
|
|
15
|
+
if (matchingSources.length > 1) {
|
|
16
|
+
return failure('workspace-preset-selection.ambiguous', `Workspace preset identity "${request.presetId}" is ambiguous.`);
|
|
17
|
+
}
|
|
18
|
+
const source = matchingSources[0];
|
|
19
|
+
if (request.expectedVersion !== null && source.version !== request.expectedVersion) {
|
|
20
|
+
return failure('workspace-preset-selection.version-incompatible', `Workspace preset "${request.presetId}" resolved to ${source.version}, not ${request.expectedVersion}.`);
|
|
21
|
+
}
|
|
22
|
+
const diagnostics = evaluateWorkspacePresetCompatibility(source, input.compatibilityRequest ?? {});
|
|
23
|
+
if (diagnostics.length > 0) {
|
|
24
|
+
return {
|
|
25
|
+
ok: false,
|
|
26
|
+
code: 'workspace-preset-selection.incompatible',
|
|
27
|
+
message: diagnostics.map(diagnostic => diagnostic.message).join(' '),
|
|
28
|
+
diagnostics
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
ok: true,
|
|
33
|
+
selection: {
|
|
34
|
+
kind: 'workspace-preset-source',
|
|
35
|
+
request,
|
|
36
|
+
source,
|
|
37
|
+
toolCatalog: candidates.toolCatalog,
|
|
38
|
+
shellWidgetCatalog: candidates.shellWidgetCatalog
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function failure(code, message) {
|
|
43
|
+
return { ok: false, code, message, diagnostics: [] };
|
|
44
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { listPanelsInWorkspace } from "../../domain/workspace/selectors.js";
|
|
2
|
+
import { pruneUnusedToolInstances } from "./finalizeWorkspace.js";
|
|
3
|
+
import { updatePanelById } from "./layoutTree.js";
|
|
4
|
+
export function unassignTool(workspace, toolId) {
|
|
5
|
+
const instanceIds = new Set(Object.values(workspace.toolInstances)
|
|
6
|
+
.filter(instance => instance.toolId === toolId)
|
|
7
|
+
.map(instance => instance.id));
|
|
8
|
+
if (instanceIds.size === 0)
|
|
9
|
+
return workspace;
|
|
10
|
+
let windows = workspace.windows;
|
|
11
|
+
for (const panel of listPanelsInWorkspace(workspace)) {
|
|
12
|
+
if (!panel.toolInstanceId || !instanceIds.has(panel.toolInstanceId))
|
|
13
|
+
continue;
|
|
14
|
+
windows = windows.map(window => ({
|
|
15
|
+
...window,
|
|
16
|
+
root: updatePanelById(window.root, panel.id, current => ({
|
|
17
|
+
...current,
|
|
18
|
+
title: 'Welcome',
|
|
19
|
+
toolInstanceId: null
|
|
20
|
+
}))
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
return pruneUnusedToolInstances({ ...workspace, windows }, instanceIds);
|
|
24
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type WorkspacePresetArtifact } from "./presetArtifacts.js";
|
|
2
|
+
import type { Workspace } from "../../domain/workspace/model.js";
|
|
3
|
+
import type { WorkspaceFocus } from "../../domain/workspace/session.js";
|
|
4
|
+
import type { ShellState } from "../../domain/shell/model.js";
|
|
5
|
+
export interface WorkspaceUsageContext {
|
|
6
|
+
fixtureSchemaVersion: string;
|
|
7
|
+
projectSchemaVersion: string;
|
|
8
|
+
hostContract: string;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkspaceUsageSnapshot {
|
|
11
|
+
schema: 'konitif.workspace-usage';
|
|
12
|
+
schemaVersion: 1;
|
|
13
|
+
source: {
|
|
14
|
+
presetId: string;
|
|
15
|
+
revision: string;
|
|
16
|
+
contentId: string;
|
|
17
|
+
};
|
|
18
|
+
context: WorkspaceUsageContext;
|
|
19
|
+
fragments: Record<string, string>;
|
|
20
|
+
parentSnapshotId: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface WorkspaceUsageFragments {
|
|
23
|
+
workspace: Workspace;
|
|
24
|
+
shell: ShellState;
|
|
25
|
+
focus: WorkspaceFocus;
|
|
26
|
+
[name: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface WorkspaceUsageBundle {
|
|
29
|
+
snapshotId: string;
|
|
30
|
+
snapshot: WorkspaceUsageSnapshot;
|
|
31
|
+
source: WorkspacePresetArtifact;
|
|
32
|
+
fragments: WorkspaceUsageFragments;
|
|
33
|
+
}
|
|
34
|
+
export interface WorkspaceUsageDiagnostic {
|
|
35
|
+
code: 'usage.invalid-shape' | 'usage.invalid-content' | 'usage.incomplete-fragments' | 'usage.incompatible-context' | 'usage.source-conflict' | 'usage.invalid-state';
|
|
36
|
+
path: string;
|
|
37
|
+
message: string;
|
|
38
|
+
}
|
|
39
|
+
export type WorkspaceUsageResult = {
|
|
40
|
+
ok: true;
|
|
41
|
+
bundle: WorkspaceUsageBundle;
|
|
42
|
+
diagnostics: [];
|
|
43
|
+
} | {
|
|
44
|
+
ok: false;
|
|
45
|
+
diagnostics: WorkspaceUsageDiagnostic[];
|
|
46
|
+
};
|
|
47
|
+
export interface WorkspaceUsageContentPort {
|
|
48
|
+
sha256(canonicalJson: string): Promise<string>;
|
|
49
|
+
}
|
|
50
|
+
export declare function serializeWorkspaceUsageContent(value: unknown): string;
|
|
51
|
+
export declare function createWorkspaceUsageBundle(input: {
|
|
52
|
+
source: WorkspacePresetArtifact;
|
|
53
|
+
context: WorkspaceUsageContext;
|
|
54
|
+
fragments: WorkspaceUsageFragments;
|
|
55
|
+
parentSnapshotId: string | null;
|
|
56
|
+
}, content: WorkspaceUsageContentPort): Promise<WorkspaceUsageResult>;
|
|
57
|
+
export declare function admitWorkspaceUsageBundle(value: unknown, context: WorkspaceUsageContext, content: WorkspaceUsageContentPort): Promise<WorkspaceUsageResult>;
|
|
58
|
+
export declare function compareWorkspaceUsageSource(previous: WorkspaceUsageSnapshot, next: WorkspaceUsageSnapshot): WorkspaceUsageDiagnostic[];
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { validateWorkspacePresetArtifact, evaluateWorkspacePresetCompatibility } from "./presetArtifacts.js";
|
|
2
|
+
export function serializeWorkspaceUsageContent(value) {
|
|
3
|
+
const ancestors = new Set();
|
|
4
|
+
function encode(entry) {
|
|
5
|
+
if (entry === null || typeof entry === 'boolean' || typeof entry === 'string')
|
|
6
|
+
return JSON.stringify(entry);
|
|
7
|
+
if (typeof entry === 'number' && Number.isFinite(entry))
|
|
8
|
+
return JSON.stringify(entry);
|
|
9
|
+
if (typeof entry !== 'object' || ancestors.has(entry))
|
|
10
|
+
throw new TypeError('Usage content must be finite acyclic JSON.');
|
|
11
|
+
if (!Array.isArray(entry) && Object.getPrototypeOf(entry) !== Object.prototype && Object.getPrototypeOf(entry) !== null) {
|
|
12
|
+
throw new TypeError('Usage objects must be plain JSON records.');
|
|
13
|
+
}
|
|
14
|
+
ancestors.add(entry);
|
|
15
|
+
const text = Array.isArray(entry)
|
|
16
|
+
? `[${Array.from({ length: entry.length }, (_, index) => {
|
|
17
|
+
const descriptor = Object.getOwnPropertyDescriptor(entry, String(index));
|
|
18
|
+
if (!descriptor || !('value' in descriptor))
|
|
19
|
+
throw new TypeError('Usage arrays must contain data indices, not accessors or holes.');
|
|
20
|
+
return encode(descriptor.value);
|
|
21
|
+
}).join(',')}]`
|
|
22
|
+
: `{${Object.keys(entry).sort().map(key => {
|
|
23
|
+
const descriptor = Object.getOwnPropertyDescriptor(entry, key);
|
|
24
|
+
if (!('value' in descriptor))
|
|
25
|
+
throw new TypeError('Usage content must not contain accessors.');
|
|
26
|
+
return `${JSON.stringify(key)}:${encode(descriptor.value)}`;
|
|
27
|
+
}).join(',')}}`;
|
|
28
|
+
ancestors.delete(entry);
|
|
29
|
+
return text;
|
|
30
|
+
}
|
|
31
|
+
return encode(value);
|
|
32
|
+
}
|
|
33
|
+
export async function createWorkspaceUsageBundle(input, content) {
|
|
34
|
+
try {
|
|
35
|
+
const frozen = JSON.parse(serializeWorkspaceUsageContent(input));
|
|
36
|
+
const references = {};
|
|
37
|
+
for (const name of Object.keys(frozen.fragments).sort()) {
|
|
38
|
+
Object.defineProperty(references, name, { value: await content.sha256(serializeWorkspaceUsageContent(frozen.fragments[name])), enumerable: true });
|
|
39
|
+
}
|
|
40
|
+
const snapshot = {
|
|
41
|
+
schema: 'konitif.workspace-usage', schemaVersion: 1,
|
|
42
|
+
source: { presetId: frozen.source.id, revision: frozen.source.version, contentId: await content.sha256(serializeWorkspaceUsageContent(frozen.source)) },
|
|
43
|
+
context: frozen.context, fragments: references, parentSnapshotId: frozen.parentSnapshotId
|
|
44
|
+
};
|
|
45
|
+
return admitWorkspaceUsageBundle({ snapshotId: await content.sha256(serializeWorkspaceUsageContent(snapshot)), snapshot, source: frozen.source, fragments: frozen.fragments }, frozen.context, content);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
return refused('usage.invalid-content', '$', String(error));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function admitWorkspaceUsageBundle(value, context, content) {
|
|
52
|
+
try {
|
|
53
|
+
const bundle = JSON.parse(serializeWorkspaceUsageContent(value));
|
|
54
|
+
const snapshot = bundle?.snapshot;
|
|
55
|
+
if (!snapshot || snapshot.schema !== 'konitif.workspace-usage' || snapshot.schemaVersion !== 1 ||
|
|
56
|
+
!isContentId(bundle.snapshotId) || !snapshot.source || !isContentId(snapshot.source.contentId) ||
|
|
57
|
+
!isRecord(snapshot.context) || !isRecord(snapshot.fragments) || !isRecord(bundle.fragments) ||
|
|
58
|
+
!(snapshot.parentSnapshotId === null || isContentId(snapshot.parentSnapshotId)))
|
|
59
|
+
return refused('usage.invalid-shape', '$', 'Invalid usage snapshot or content reference.');
|
|
60
|
+
if (Object.keys(snapshot.context).sort().join(',') !== 'fixtureSchemaVersion,hostContract,projectSchemaVersion' ||
|
|
61
|
+
Object.values(snapshot.context).some(entry => typeof entry !== 'string' || !entry) ||
|
|
62
|
+
serializeWorkspaceUsageContent(snapshot.context) !== serializeWorkspaceUsageContent(context))
|
|
63
|
+
return refused('usage.incompatible-context', '$.snapshot.context', 'Usage context does not match this host.');
|
|
64
|
+
if (Object.keys(snapshot.fragments).sort().join('\n') !== Object.keys(bundle.fragments).sort().join('\n') ||
|
|
65
|
+
['workspace', 'shell', 'focus'].some(name => !Object.prototype.hasOwnProperty.call(bundle.fragments, name)) ||
|
|
66
|
+
Object.keys(snapshot.fragments).some(name => !/^[a-zA-Z][a-zA-Z0-9.-]*$/.test(name) || !isContentId(snapshot.fragments[name])))
|
|
67
|
+
return refused('usage.incomplete-fragments', '$.fragments', 'All declared fragments, including workspace, shell and focus, are required.');
|
|
68
|
+
if (await content.sha256(serializeWorkspaceUsageContent(snapshot)) !== bundle.snapshotId)
|
|
69
|
+
return refused('usage.invalid-content', '$.snapshotId', 'Snapshot content identity mismatch.');
|
|
70
|
+
if (await content.sha256(serializeWorkspaceUsageContent(bundle.source)) !== snapshot.source.contentId)
|
|
71
|
+
return refused('usage.invalid-content', '$.source', 'Source content identity mismatch.');
|
|
72
|
+
if (bundle.source.id !== snapshot.source.presetId || bundle.source.version !== snapshot.source.revision)
|
|
73
|
+
return refused('usage.source-conflict', '$.snapshot.source', 'Source authored identity and revision must match their immutable reference.');
|
|
74
|
+
const sourceIssues = validateWorkspacePresetArtifact(bundle.source);
|
|
75
|
+
if (sourceIssues.length || evaluateWorkspacePresetCompatibility(bundle.source, context).length)
|
|
76
|
+
return refused('usage.invalid-state', '$.source', 'Invalid or incompatible authored preset.');
|
|
77
|
+
for (const name of Object.keys(snapshot.fragments)) {
|
|
78
|
+
if (await content.sha256(serializeWorkspaceUsageContent(bundle.fragments[name])) !== snapshot.fragments[name])
|
|
79
|
+
return refused('usage.invalid-content', `$.fragments.${name}`, 'Fragment content identity mismatch.');
|
|
80
|
+
}
|
|
81
|
+
const stateIssues = validateWorkspacePresetArtifact({ ...bundle.source, workspaceSession: { workspace: bundle.fragments.workspace, focus: bundle.fragments.focus }, shellState: bundle.fragments.shell });
|
|
82
|
+
if (stateIssues.length)
|
|
83
|
+
return refused('usage.invalid-state', '$.fragments', stateIssues.map(issue => `${issue.path}: ${issue.message}`).join('; '));
|
|
84
|
+
return { ok: true, bundle, diagnostics: [] };
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
return refused('usage.invalid-content', '$', String(error));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export function compareWorkspaceUsageSource(previous, next) {
|
|
91
|
+
return previous.source.presetId === next.source.presetId && previous.source.revision === next.source.revision && previous.source.contentId !== next.source.contentId
|
|
92
|
+
? [{ code: 'usage.source-conflict', path: '$.source', message: 'An authored identity/revision must not be rebound to different content.' }]
|
|
93
|
+
: [];
|
|
94
|
+
}
|
|
95
|
+
function isContentId(value) {
|
|
96
|
+
return typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
|
|
97
|
+
}
|
|
98
|
+
function isRecord(value) {
|
|
99
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
100
|
+
}
|
|
101
|
+
function refused(code, path, message) {
|
|
102
|
+
return { ok: false, diagnostics: [{ code, path, message }] };
|
|
103
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ShellWidgetCatalogPort } from "../../domain/shell/model.js";
|
|
2
|
+
import type { ToolCatalogPort, ToolDefinition } from "../../domain/tool/model.js";
|
|
3
|
+
import type { WorkspacePresetArtifact } from "./presetArtifacts.js";
|
|
4
|
+
export type WorkspacePresetCatalogAdmissionIssue = {
|
|
5
|
+
code: 'workspace-preset-admission.missing-tool';
|
|
6
|
+
subjectId: string;
|
|
7
|
+
message: string;
|
|
8
|
+
} | {
|
|
9
|
+
code: 'workspace-preset-admission.missing-widget';
|
|
10
|
+
subjectId: string;
|
|
11
|
+
message: string;
|
|
12
|
+
} | {
|
|
13
|
+
code: 'workspace-preset-admission.missing-capability' | 'workspace-preset-admission.incompatible-capability';
|
|
14
|
+
subjectId: string;
|
|
15
|
+
consumerToolId: string;
|
|
16
|
+
versionRange: string;
|
|
17
|
+
availableVersions: string[];
|
|
18
|
+
message: string;
|
|
19
|
+
};
|
|
20
|
+
export type WorkspacePresetCatalogAdmission = {
|
|
21
|
+
ok: true;
|
|
22
|
+
issues: [];
|
|
23
|
+
} | {
|
|
24
|
+
ok: false;
|
|
25
|
+
issues: WorkspacePresetCatalogAdmissionIssue[];
|
|
26
|
+
};
|
|
27
|
+
export declare function evaluateWorkspacePresetCatalogAdmission(input: {
|
|
28
|
+
source: WorkspacePresetArtifact;
|
|
29
|
+
toolCatalog: ToolCatalogPort;
|
|
30
|
+
shellWidgetCatalog: ShellWidgetCatalogPort;
|
|
31
|
+
availableToolDefinitions?: readonly ToolDefinition[];
|
|
32
|
+
}): WorkspacePresetCatalogAdmission;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { isKonitifCapabilityVersionCompatible } from '@konitif/core';
|
|
2
|
+
export function evaluateWorkspacePresetCatalogAdmission(input) {
|
|
3
|
+
const issues = [];
|
|
4
|
+
const requiredToolIds = unique(Object.values(input.source.workspaceSession.workspace.toolInstances).map(instance => instance.toolId));
|
|
5
|
+
const requiredWidgetIds = unique(Object.values(input.source.shellState.regions).flatMap(region => region.widgetIds));
|
|
6
|
+
const requiredDefinitions = requiredToolIds.flatMap(toolId => {
|
|
7
|
+
const definition = input.toolCatalog.getDefinition(toolId);
|
|
8
|
+
if (!definition) {
|
|
9
|
+
issues.push({
|
|
10
|
+
code: 'workspace-preset-admission.missing-tool',
|
|
11
|
+
subjectId: toolId,
|
|
12
|
+
message: `Required tool "${toolId}" is not available in the admitted catalog.`
|
|
13
|
+
});
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
return [definition];
|
|
17
|
+
});
|
|
18
|
+
for (const widgetId of requiredWidgetIds) {
|
|
19
|
+
if (!input.shellWidgetCatalog.getDefinition(widgetId)) {
|
|
20
|
+
issues.push({
|
|
21
|
+
code: 'workspace-preset-admission.missing-widget',
|
|
22
|
+
subjectId: widgetId,
|
|
23
|
+
message: `Required widget "${widgetId}" is not available in the admitted catalog.`
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const availableDefinitions = input.availableToolDefinitions ?? requiredDefinitions;
|
|
28
|
+
for (const consumer of requiredDefinitions) {
|
|
29
|
+
for (const requirement of consumer.capabilities?.consumes ?? []) {
|
|
30
|
+
if (requirement.mode !== 'required')
|
|
31
|
+
continue;
|
|
32
|
+
const providers = availableDefinitions.flatMap(definition => (definition.capabilities?.provides ?? [])
|
|
33
|
+
.filter(capability => capability.id === requirement.id)
|
|
34
|
+
.map(capability => ({ definition, capability })));
|
|
35
|
+
if (providers.some(provider => isKonitifCapabilityVersionCompatible(provider.capability.version, requirement.versionRange))) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const availableVersions = unique(providers.map(provider => provider.capability.version));
|
|
39
|
+
const incompatible = availableVersions.length > 0;
|
|
40
|
+
issues.push({
|
|
41
|
+
code: incompatible
|
|
42
|
+
? 'workspace-preset-admission.incompatible-capability'
|
|
43
|
+
: 'workspace-preset-admission.missing-capability',
|
|
44
|
+
subjectId: requirement.id,
|
|
45
|
+
consumerToolId: consumer.id,
|
|
46
|
+
versionRange: requirement.versionRange,
|
|
47
|
+
availableVersions,
|
|
48
|
+
message: incompatible
|
|
49
|
+
? `Tool "${consumer.id}" requires ${requirement.id}@${requirement.versionRange}; available versions are ${availableVersions.join(', ')}.`
|
|
50
|
+
: `Tool "${consumer.id}" requires ${requirement.id}@${requirement.versionRange}; no provider is available.`
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return issues.length === 0 ? { ok: true, issues: [] } : { ok: false, issues };
|
|
55
|
+
}
|
|
56
|
+
function unique(values) {
|
|
57
|
+
return [...new Set(values)];
|
|
58
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface WorkbenchCatalogMedia {
|
|
2
|
+
src: string;
|
|
3
|
+
alt: string;
|
|
4
|
+
mediaType: 'image/svg+xml' | 'image/png' | 'image/webp' | 'image/jpeg';
|
|
5
|
+
}
|
|
6
|
+
export interface WorkbenchCatalogDependency {
|
|
7
|
+
packageName: string;
|
|
8
|
+
versionRange: string;
|
|
9
|
+
scope: 'runtime' | 'peer' | 'optional' | 'development';
|
|
10
|
+
family: 'konitif' | 'product' | 'external';
|
|
11
|
+
sourceUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface WorkbenchCatalogDiagramNode {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
description: string;
|
|
17
|
+
tone?: 'projection' | 'runtime' | 'observation';
|
|
18
|
+
}
|
|
19
|
+
export interface WorkbenchCatalogDiagramEdge {
|
|
20
|
+
from: string;
|
|
21
|
+
to: string;
|
|
22
|
+
}
|
|
23
|
+
export interface WorkbenchCatalogDiagram {
|
|
24
|
+
id: string;
|
|
25
|
+
title: string;
|
|
26
|
+
kind: string;
|
|
27
|
+
summary: string;
|
|
28
|
+
sourcePackage: string;
|
|
29
|
+
nodes: readonly WorkbenchCatalogDiagramNode[];
|
|
30
|
+
edges: readonly WorkbenchCatalogDiagramEdge[];
|
|
31
|
+
caption: string;
|
|
32
|
+
}
|
|
33
|
+
export interface WorkbenchCatalogPresentation {
|
|
34
|
+
summary?: string;
|
|
35
|
+
icon?: WorkbenchCatalogMedia;
|
|
36
|
+
illustration?: WorkbenchCatalogMedia;
|
|
37
|
+
metadata?: Readonly<Record<string, string>>;
|
|
38
|
+
detailedDescription?: string;
|
|
39
|
+
dependencies?: readonly WorkbenchCatalogDependency[];
|
|
40
|
+
diagram?: WorkbenchCatalogDiagram;
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { WorkbenchIconInput } from "../icon/model.js";
|
|
2
|
+
import type { WorkbenchCatalogElementVersion } from "../catalog/version.js";
|
|
3
|
+
import type { WorkbenchCatalogPresentation } from "../catalog/presentation.js";
|
|
2
4
|
export type ShellRegionId = 'left' | 'right' | 'bottom';
|
|
3
5
|
export type ShellRegionPresentation = 'tabs' | 'stack';
|
|
4
6
|
export type ShellRegionAxis = 'horizontal' | 'vertical';
|
|
@@ -13,6 +15,8 @@ export interface ShellRegionState {
|
|
|
13
15
|
hiddenWidgetIds?: string[];
|
|
14
16
|
presentation?: ShellRegionPresentation;
|
|
15
17
|
axis?: ShellRegionAxis;
|
|
18
|
+
/** Relative track sizes, keyed by widget identity. */
|
|
19
|
+
widgetProportions?: Record<string, number>;
|
|
16
20
|
}
|
|
17
21
|
export interface ShellState {
|
|
18
22
|
regions: Record<ShellRegionId, ShellRegionState>;
|
|
@@ -23,6 +27,8 @@ export interface ShellWidgetPlacement {
|
|
|
23
27
|
}
|
|
24
28
|
export interface ShellWidgetDefinition {
|
|
25
29
|
id: string;
|
|
30
|
+
version?: WorkbenchCatalogElementVersion;
|
|
31
|
+
catalogPresentation?: WorkbenchCatalogPresentation;
|
|
26
32
|
capabilityId?: string | null;
|
|
27
33
|
title: string;
|
|
28
34
|
icon?: WorkbenchIconInput;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { JsonObject } from "../shared/json.js";
|
|
2
2
|
import type { KonitifToolCapabilities } from '@konitif/core';
|
|
3
|
+
import type { WorkbenchCatalogPresentation } from "../catalog/presentation.js";
|
|
3
4
|
import type { WorkbenchIconInput } from "../icon/model.js";
|
|
5
|
+
import type { WorkbenchCatalogElementVersion } from "../catalog/version.js";
|
|
4
6
|
import type { ShellRegionId } from "../shell/model.js";
|
|
5
7
|
import type { WorkbenchReadingLevel, WorkbenchReadingLevelPresentation } from "../presentation/readingLevel.js";
|
|
6
8
|
export type ToolOpeningPolicy = {
|
|
@@ -117,6 +119,8 @@ export interface ToolFullscreenCompanionChromePolicy {
|
|
|
117
119
|
export interface ToolFullscreenCompanionBehaviorPolicy {
|
|
118
120
|
pinned?: boolean;
|
|
119
121
|
draggable?: boolean;
|
|
122
|
+
/** Allow the host's anchored, presentation-only resize handles. */
|
|
123
|
+
resizable?: boolean;
|
|
120
124
|
collapsible?: boolean;
|
|
121
125
|
passthroughWhenCollapsed?: boolean;
|
|
122
126
|
}
|
|
@@ -137,6 +141,8 @@ export interface ToolFullscreenCompanionDefinition {
|
|
|
137
141
|
}
|
|
138
142
|
export interface ToolDefinition {
|
|
139
143
|
id: string;
|
|
144
|
+
version?: WorkbenchCatalogElementVersion;
|
|
145
|
+
catalogPresentation?: WorkbenchCatalogPresentation;
|
|
140
146
|
title: string;
|
|
141
147
|
icon?: WorkbenchIconInput;
|
|
142
148
|
/**
|
package/dist/hosting.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { exportWorkspaceSnapshot, importWorkspaceSnapshot } from "./application/
|
|
|
8
8
|
export { listPanelsInWorkspace } from "./domain/workspace/selectors.js";
|
|
9
9
|
export { validateWorkspace } from "./domain/workspace/validation.js";
|
|
10
10
|
export { InMemoryToolRegistry } from "./infrastructure/tools/InMemoryToolRegistry.js";
|
|
11
|
+
export type { RegisteredToolComponentLoader } from "./infrastructure/tools/InMemoryToolRegistry.js";
|
|
11
12
|
export type { Workspace } from "./domain/workspace/model.js";
|
|
12
13
|
export type { WorkspacePersistencePort } from "./domain/workspace/persistence.js";
|
|
13
14
|
export type { ToolDefinition, ToolInstance, ToolCatalogPort } from "./domain/tool/model.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export * from "./boot/index.js";
|
|
2
|
+
export * from "./application/launch/workbenchLaunchPolicy.js";
|
|
3
|
+
export * from "./application/launch/catalogShareReference.js";
|
|
2
4
|
export * from "./application/widgets/createWorkbenchWidgetAdapter.js";
|
|
5
|
+
export * from "./application/tool/experienceToolSelection.js";
|
|
3
6
|
export * from "./stage/index.js";
|
|
4
7
|
export * from "./application/shell/state.js";
|
|
5
8
|
export * from "./application/layout/surfaceCommandRouter.js";
|
|
@@ -18,6 +21,7 @@ export * from "./application/workspace/layoutInteraction.js";
|
|
|
18
21
|
export * from "./application/workspace/openTool.js";
|
|
19
22
|
export * from "./application/workspace/presets.js";
|
|
20
23
|
export * from "./application/workspace/presetArtifacts.js";
|
|
24
|
+
export * from "./application/workspace/presetSelection.js";
|
|
21
25
|
export * from "./application/workspace/resizeSplit.js";
|
|
22
26
|
export * from "./application/workspace/resizeSplitBoundary.js";
|
|
23
27
|
export * from "./application/workspace/runToolShellCommand.js";
|
|
@@ -36,6 +40,11 @@ export * from "./application/workspace/swapPanelArea.js";
|
|
|
36
40
|
export * from "./application/workspace/toolRuntime.js";
|
|
37
41
|
export * from "./application/workspace/toolCapabilities.js";
|
|
38
42
|
export * from "./application/workspace/updateToolState.js";
|
|
43
|
+
export * from "./application/workspace/unassignTool.js";
|
|
44
|
+
export * from "./application/workspace/parkedTool.js";
|
|
45
|
+
export * from "./application/workspace/workspacePresetCatalogAdmission.js";
|
|
46
|
+
export * from "./domain/catalog/version.js";
|
|
47
|
+
export * from "./domain/catalog/presentation.js";
|
|
39
48
|
export * from "./application/project/ProjectManifest.js";
|
|
40
49
|
export * from "./application/runtime/WorkbenchRuntimeCore.js";
|
|
41
50
|
export * from "./application/runtime/RuntimeObservabilityHub.js";
|
|
@@ -109,3 +118,4 @@ export * from "./infrastructure/workbenches/InMemoryWorkbenchRegistry.js";
|
|
|
109
118
|
export * from "./infrastructure/widgets/InMemoryShellWidgetRegistry.js";
|
|
110
119
|
export * from "./infrastructure/widgets/InMemoryWorkbenchWidgetRegistry.js";
|
|
111
120
|
export * from "./temporal/index.js";
|
|
121
|
+
export * from "./application/workspace/usageSnapshots.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export * from "./boot/index.js";
|
|
2
|
+
export * from "./application/launch/workbenchLaunchPolicy.js";
|
|
3
|
+
export * from "./application/launch/catalogShareReference.js";
|
|
2
4
|
export * from "./application/widgets/createWorkbenchWidgetAdapter.js";
|
|
5
|
+
export * from "./application/tool/experienceToolSelection.js";
|
|
3
6
|
export * from "./stage/index.js";
|
|
4
7
|
export * from "./application/shell/state.js";
|
|
5
8
|
export * from "./application/layout/surfaceCommandRouter.js";
|
|
@@ -18,6 +21,7 @@ export * from "./application/workspace/layoutInteraction.js";
|
|
|
18
21
|
export * from "./application/workspace/openTool.js";
|
|
19
22
|
export * from "./application/workspace/presets.js";
|
|
20
23
|
export * from "./application/workspace/presetArtifacts.js";
|
|
24
|
+
export * from "./application/workspace/presetSelection.js";
|
|
21
25
|
export * from "./application/workspace/resizeSplit.js";
|
|
22
26
|
export * from "./application/workspace/resizeSplitBoundary.js";
|
|
23
27
|
export * from "./application/workspace/runToolShellCommand.js";
|
|
@@ -36,6 +40,11 @@ export * from "./application/workspace/swapPanelArea.js";
|
|
|
36
40
|
export * from "./application/workspace/toolRuntime.js";
|
|
37
41
|
export * from "./application/workspace/toolCapabilities.js";
|
|
38
42
|
export * from "./application/workspace/updateToolState.js";
|
|
43
|
+
export * from "./application/workspace/unassignTool.js";
|
|
44
|
+
export * from "./application/workspace/parkedTool.js";
|
|
45
|
+
export * from "./application/workspace/workspacePresetCatalogAdmission.js";
|
|
46
|
+
export * from "./domain/catalog/version.js";
|
|
47
|
+
export * from "./domain/catalog/presentation.js";
|
|
39
48
|
export * from "./application/project/ProjectManifest.js";
|
|
40
49
|
export * from "./application/runtime/WorkbenchRuntimeCore.js";
|
|
41
50
|
export * from "./application/runtime/RuntimeObservabilityHub.js";
|
|
@@ -109,3 +118,4 @@ export * from "./infrastructure/workbenches/InMemoryWorkbenchRegistry.js";
|
|
|
109
118
|
export * from "./infrastructure/widgets/InMemoryShellWidgetRegistry.js";
|
|
110
119
|
export * from "./infrastructure/widgets/InMemoryWorkbenchWidgetRegistry.js";
|
|
111
120
|
export * from "./temporal/index.js";
|
|
121
|
+
export * from "./application/workspace/usageSnapshots.js";
|
|
@@ -41,6 +41,7 @@ export declare class InMemoryToolRegistry implements ToolCatalogPort {
|
|
|
41
41
|
register(entry: RegisteredTool): void;
|
|
42
42
|
get(toolId: string): RegisteredTool | undefined;
|
|
43
43
|
getDefinition(toolId: string): ToolDefinition | undefined;
|
|
44
|
+
unregister(toolId: string): boolean;
|
|
44
45
|
list(): RegisteredTool[];
|
|
45
46
|
listDiagnostics(): ToolRegistryDuplicateDiagnostic[];
|
|
46
47
|
}
|
|
@@ -51,6 +51,17 @@ export class InMemoryToolRegistry {
|
|
|
51
51
|
getDefinition(toolId) {
|
|
52
52
|
return this.entries.get(toolId)?.definition;
|
|
53
53
|
}
|
|
54
|
+
unregister(toolId) {
|
|
55
|
+
if (!this.entries.delete(toolId))
|
|
56
|
+
return false;
|
|
57
|
+
this.registrationOrders.delete(toolId);
|
|
58
|
+
this.revision += 1;
|
|
59
|
+
for (const listener of [...this.revisionListeners]) {
|
|
60
|
+
if (this.revisionListeners.has(listener))
|
|
61
|
+
listener(this.revision);
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
54
65
|
list() {
|
|
55
66
|
return [...this.entries.values()];
|
|
56
67
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { setShellRegionArrangement, createShellState, setShellRegionVisible, setShellRegionOpen, setShellRegionSize, activateShellWidget, setShellRegionWidgetVisible, addShellWidgetToRegion, moveShellWidgetToRegion, removeShellWidgetFromRegion } from "./application/shell/state.js";
|
|
1
|
+
export { setShellRegionArrangement, setShellRegionWidgetProportions, createShellState, setShellRegionVisible, setShellRegionOpen, setShellRegionSize, activateShellWidget, setShellRegionWidgetVisible, addShellWidgetToRegion, moveShellWidgetToRegion, removeShellWidgetFromRegion } from "./application/shell/state.js";
|
|
2
2
|
export { collapseSplit } from "./application/workspace/collapseSplit.js";
|
|
3
3
|
export { collapseSplitBoundary } from "./application/workspace/collapseSplitBoundary.js";
|
|
4
4
|
export { dispatchWorkspaceCommand, type WorkspaceCommand } from "./application/workspace/commands.js";
|
|
@@ -34,3 +34,20 @@ export { type Workspace } from "./domain/workspace/model.js";
|
|
|
34
34
|
export { type WorkspacePersistencePort } from "./domain/workspace/persistence.js";
|
|
35
35
|
export { findPanelInWorkspace, getPreferredFocusedPanelId } from "./domain/workspace/selectors.js";
|
|
36
36
|
export { validateWorkspace } from "./domain/workspace/validation.js";
|
|
37
|
+
export * from "./application/workspace/presetArtifacts.js";
|
|
38
|
+
export * from "./application/workspace/presetSelection.js";
|
|
39
|
+
export * from "./application/workspace/usageSnapshots.js";
|
|
40
|
+
export * from "./application/launch/workbenchLaunchPolicy.js";
|
|
41
|
+
export * from "./application/tool/experienceToolSelection.js";
|
|
42
|
+
export * from "./application/workspace/unassignTool.js";
|
|
43
|
+
export * from "./application/workspace/parkedTool.js";
|
|
44
|
+
export * from "./application/workspace/workspacePresetCatalogAdmission.js";
|
|
45
|
+
export type { WorkbenchCatalogElementVersion } from "./domain/catalog/version.js";
|
|
46
|
+
export type { WorkbenchCatalogMedia, WorkbenchCatalogPresentation } from "./domain/catalog/presentation.js";
|
|
47
|
+
export { createPanel, createStack, createSplit, createWorkspaceWindow, createWorkspaceShell } from "./domain/workspace/factories.js";
|
|
48
|
+
export { cloneJsonObject } from "./domain/shared/json.js";
|
|
49
|
+
export { createInitialToolShellState } from "./domain/tool/shell.js";
|
|
50
|
+
export { patchWorkbenchToolDockVisibilityState } from "./domain/widget/toolDock.js";
|
|
51
|
+
export type { ToolDefinition, ToolInstance, ToolCatalogPort } from "./domain/tool/model.js";
|
|
52
|
+
export type { ShellWidgetCatalogPort } from "./domain/shell/model.js";
|
|
53
|
+
export { InMemoryToolRegistry } from "./infrastructure/tools/InMemoryToolRegistry.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Explicit workspace contracts for state orchestration. No host adapters.
|
|
2
2
|
// Reexports preserve the authority and identity of the existing implementations.
|
|
3
|
-
export { setShellRegionArrangement, createShellState, setShellRegionVisible, setShellRegionOpen, setShellRegionSize, activateShellWidget, setShellRegionWidgetVisible, addShellWidgetToRegion, moveShellWidgetToRegion, removeShellWidgetFromRegion } from "./application/shell/state.js";
|
|
3
|
+
export { setShellRegionArrangement, setShellRegionWidgetProportions, createShellState, setShellRegionVisible, setShellRegionOpen, setShellRegionSize, activateShellWidget, setShellRegionWidgetVisible, addShellWidgetToRegion, moveShellWidgetToRegion, removeShellWidgetFromRegion } from "./application/shell/state.js";
|
|
4
4
|
export { collapseSplit } from "./application/workspace/collapseSplit.js";
|
|
5
5
|
export { collapseSplitBoundary } from "./application/workspace/collapseSplitBoundary.js";
|
|
6
6
|
export { dispatchWorkspaceCommand } from "./application/workspace/commands.js";
|
|
@@ -26,3 +26,17 @@ export { updateToolState } from "./application/workspace/updateToolState.js";
|
|
|
26
26
|
export { normalizeToolShellStatus, normalizeToolPanelLoadingState, normalizeToolResourceLoadingState } from "./domain/tool/runtime.js";
|
|
27
27
|
export { findPanelInWorkspace, getPreferredFocusedPanelId } from "./domain/workspace/selectors.js";
|
|
28
28
|
export { validateWorkspace } from "./domain/workspace/validation.js";
|
|
29
|
+
export * from "./application/workspace/presetArtifacts.js";
|
|
30
|
+
export * from "./application/workspace/presetSelection.js";
|
|
31
|
+
export * from "./application/workspace/usageSnapshots.js";
|
|
32
|
+
export * from "./application/launch/workbenchLaunchPolicy.js";
|
|
33
|
+
export * from "./application/tool/experienceToolSelection.js";
|
|
34
|
+
export * from "./application/workspace/unassignTool.js";
|
|
35
|
+
export * from "./application/workspace/parkedTool.js";
|
|
36
|
+
export * from "./application/workspace/workspacePresetCatalogAdmission.js";
|
|
37
|
+
// Authored seed construction uses the same factories as the complete Workbench.
|
|
38
|
+
export { createPanel, createStack, createSplit, createWorkspaceWindow, createWorkspaceShell } from "./domain/workspace/factories.js";
|
|
39
|
+
export { cloneJsonObject } from "./domain/shared/json.js";
|
|
40
|
+
export { createInitialToolShellState } from "./domain/tool/shell.js";
|
|
41
|
+
export { patchWorkbenchToolDockVisibilityState } from "./domain/widget/toolDock.js";
|
|
42
|
+
export { InMemoryToolRegistry } from "./infrastructure/tools/InMemoryToolRegistry.js";
|
package/package.json
CHANGED