@dxos/plugin-devtools 0.11.1 → 0.12.0-next-2800d0376abcc62d5544afdd744b16b2b960434e
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/dist/lib/{chunk-types.mjs → Devtools.mjs} +5 -14
- package/dist/lib/Devtools.mjs.map +1 -0
- package/dist/lib/DevtoolsEvents.mjs +15 -0
- package/dist/lib/DevtoolsEvents.mjs.map +1 -0
- package/dist/lib/DevtoolsPlugin.mjs +13 -36
- package/dist/lib/DevtoolsPlugin.mjs.map +1 -1
- package/dist/lib/capabilities.mjs +39 -5
- package/dist/lib/capabilities.mjs.map +1 -1
- package/dist/lib/capabilities.node.mjs +34 -0
- package/dist/lib/capabilities.node.mjs.map +1 -0
- package/dist/lib/capabilities.workerd.mjs +30 -0
- package/dist/lib/capabilities.workerd.mjs.map +1 -0
- package/dist/lib/chunk-CliPanel.mjs +103 -0
- package/dist/lib/chunk-CliPanel.mjs.map +1 -0
- package/dist/lib/chunk-DebugGraph.mjs +2 -2
- package/dist/lib/chunk-DebugGraph.mjs.map +1 -1
- package/dist/lib/chunk-DevtoolsOverviewContainer.mjs +14 -13
- package/dist/lib/chunk-DevtoolsOverviewContainer.mjs.map +1 -1
- package/dist/lib/chunk-GithubPanel.mjs +36 -30
- package/dist/lib/chunk-GithubPanel.mjs.map +1 -1
- package/dist/lib/chunk-RegistryPanel.mjs +2 -2
- package/dist/lib/chunk-RegistryPanel.mjs.map +1 -1
- package/dist/lib/chunk-app-graph-builder.mjs +66 -50
- package/dist/lib/chunk-app-graph-builder.mjs.map +1 -1
- package/dist/lib/chunk-react-context.mjs +4 -3
- package/dist/lib/chunk-react-context.mjs.map +1 -1
- package/dist/lib/chunk-react-surface.mjs +35 -28
- package/dist/lib/chunk-react-surface.mjs.map +1 -1
- package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
- package/dist/lib/containers.mjs +2 -1
- package/dist/lib/containers.mjs.map +1 -1
- package/dist/lib/index.mjs +4 -3
- package/dist/lib/meta.mjs +29 -1
- package/dist/lib/meta.mjs.map +1 -0
- package/dist/lib/plugin.mjs +6 -5
- package/dist/lib/plugin.mjs.map +1 -1
- package/dist/lib/translations.mjs +1 -0
- package/dist/lib/translations.mjs.map +1 -1
- package/dist/lib/types.mjs +3 -2
- package/dist/types/src/DevtoolsPlugin.d.ts +36 -3
- package/dist/types/src/DevtoolsPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/DevtoolsSurfaces.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +2 -2
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/gen/node.d.ts +8 -0
- package/dist/types/src/capabilities/gen/node.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/workerd.d.ts +8 -0
- package/dist/types/src/capabilities/gen/workerd.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +7 -4
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-context.d.ts +2 -2
- package/dist/types/src/capabilities/react-context.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +2 -2
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/containers/CliPanel/CliPanel.d.ts +6 -0
- package/dist/types/src/containers/CliPanel/CliPanel.d.ts.map +1 -0
- package/dist/types/src/containers/CliPanel/CliPanel.stories.d.ts +89 -0
- package/dist/types/src/containers/CliPanel/CliPanel.stories.d.ts.map +1 -0
- package/dist/types/src/containers/CliPanel/index.d.ts +2 -0
- package/dist/types/src/containers/CliPanel/index.d.ts.map +1 -0
- package/dist/types/src/containers/CliPanel/useCliApp.d.ts +29 -0
- package/dist/types/src/containers/CliPanel/useCliApp.d.ts.map +1 -0
- package/dist/types/src/containers/DebugGraph/DebugGraph.d.ts +2 -2
- package/dist/types/src/containers/DebugGraph/DebugGraph.d.ts.map +1 -1
- package/dist/types/src/containers/GithubPanel/GithubComponent.d.ts +9 -4
- package/dist/types/src/containers/GithubPanel/GithubComponent.d.ts.map +1 -1
- package/dist/types/src/containers/GithubPanel/GithubComponent.stories.d.ts +6 -4
- package/dist/types/src/containers/GithubPanel/GithubComponent.stories.d.ts.map +1 -1
- package/dist/types/src/containers/index.d.ts +1 -0
- package/dist/types/src/containers/index.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/plugin.d.ts +2 -1
- package/dist/types/src/plugin.d.ts.map +1 -1
- package/dist/types/src/plugin.test.d.ts +2 -0
- package/dist/types/src/plugin.test.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +6 -4
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Devtools.d.ts +1 -0
- package/dist/types/src/types/Devtools.d.ts.map +1 -1
- package/dist/types/src/types/DevtoolsEvents.d.ts +8 -0
- package/dist/types/src/types/DevtoolsEvents.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +1 -0
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +73 -74
- package/src/DevtoolsPlugin.ts +15 -0
- package/src/capabilities/DevtoolsSurfaces.tsx +9 -4
- package/src/capabilities/app-graph-builder.ts +72 -49
- package/src/capabilities/gen/node.ts +49 -0
- package/src/capabilities/gen/workerd.ts +44 -0
- package/src/capabilities/index.ts +50 -4
- package/src/capabilities/react-context.tsx +3 -2
- package/src/capabilities/react-surface.ts +11 -5
- package/src/containers/CliPanel/CliPanel.stories.tsx +75 -0
- package/src/containers/CliPanel/CliPanel.tsx +45 -0
- package/src/containers/CliPanel/index.ts +5 -0
- package/src/containers/CliPanel/useCliApp.ts +103 -0
- package/src/containers/DebugGraph/DebugGraph.tsx +3 -3
- package/src/containers/GithubPanel/GithubComponent.tsx +13 -15
- package/src/containers/RegistryPanel/RegistryPanel.tsx +1 -1
- package/src/containers/index.ts +1 -0
- package/src/index.ts +3 -2
- package/src/meta.ts +1 -1
- package/src/{DevtoolsPlugin.test.ts → plugin.test.ts} +5 -4
- package/src/plugin.tsx +20 -0
- package/src/translations.ts +1 -0
- package/src/types/Devtools.ts +1 -0
- package/src/types/DevtoolsEvents.ts +16 -0
- package/src/types/index.ts +1 -0
- package/dist/lib/DevtoolsPlugin.node.mjs +0 -10
- package/dist/lib/DevtoolsPlugin.node.mjs.map +0 -1
- package/dist/lib/DevtoolsPlugin.workerd.mjs +0 -8
- package/dist/lib/DevtoolsPlugin.workerd.mjs.map +0 -1
- package/dist/lib/chunk-meta.mjs +0 -30
- package/dist/lib/chunk-meta.mjs.map +0 -1
- package/dist/lib/chunk-types.mjs.map +0 -1
- package/dist/types/src/DevtoolsPlugin.node.d.ts +0 -4
- package/dist/types/src/DevtoolsPlugin.node.d.ts.map +0 -1
- package/dist/types/src/DevtoolsPlugin.test.d.ts +0 -2
- package/dist/types/src/DevtoolsPlugin.test.d.ts.map +0 -1
- package/dist/types/src/DevtoolsPlugin.workerd.d.ts +0 -4
- package/dist/types/src/DevtoolsPlugin.workerd.d.ts.map +0 -1
- package/src/DevtoolsPlugin.node.ts +0 -16
- package/src/DevtoolsPlugin.tsx +0 -54
- package/src/DevtoolsPlugin.workerd.ts +0 -11
- package/src/plugin.ts +0 -9
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'node' condition.
|
|
6
|
+
|
|
7
|
+
import { create } from '@bufbuild/protobuf';
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
9
|
+
|
|
10
|
+
import * as Capability from '@dxos/app-framework/Capability';
|
|
11
|
+
import * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';
|
|
12
|
+
import * as AppCapability from '@dxos/app-toolkit/AppCapability';
|
|
13
|
+
import { Runtime_Client_StorageSchema } from '@dxos/protocols/buf/dxos/config_pb';
|
|
14
|
+
import { type Client } from '@dxos/react-client';
|
|
15
|
+
|
|
16
|
+
import { translations } from '#translations';
|
|
17
|
+
|
|
18
|
+
const setupDevtools = () => {
|
|
19
|
+
(globalThis as any).composer ??= {};
|
|
20
|
+
|
|
21
|
+
// Used to test how composer handles breaking protocol changes.
|
|
22
|
+
(globalThis as any).composer.changeStorageVersionInMetadata = async (version: number) => {
|
|
23
|
+
const { changeStorageVersionInMetadata } = await import('@dxos/client-services/testing');
|
|
24
|
+
const { createStorageObjects } = await import('@dxos/client-services');
|
|
25
|
+
const client: Client = (window as any).dxos.client;
|
|
26
|
+
const config = client.config;
|
|
27
|
+
await client.destroy();
|
|
28
|
+
const { storage } = createStorageObjects(
|
|
29
|
+
config.values?.runtime?.client?.storage ?? create(Runtime_Client_StorageSchema, {}),
|
|
30
|
+
);
|
|
31
|
+
await changeStorageVersionInMetadata(storage, version);
|
|
32
|
+
location.pathname = '/';
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const AppGraphBuilder = AppCapability.appGraphBuilder(() => import('../app-graph-builder'), {
|
|
37
|
+
requires: [AppCapabilities.AppGraph],
|
|
38
|
+
environments: ['node'],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export const SetupDevtools = Capability.inlineModule('setup-devtools', { provides: [] }, () =>
|
|
42
|
+
Effect.sync(() => setupDevtools()),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
export const Translations = AppCapability.translations(translations);
|
|
46
|
+
|
|
47
|
+
export const PluginAsset = undefined;
|
|
48
|
+
export const ReactContext = undefined;
|
|
49
|
+
export const ReactSurface = undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'workerd' condition.
|
|
6
|
+
|
|
7
|
+
import { create } from '@bufbuild/protobuf';
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
9
|
+
|
|
10
|
+
import * as Capability from '@dxos/app-framework/Capability';
|
|
11
|
+
import * as AppCapability from '@dxos/app-toolkit/AppCapability';
|
|
12
|
+
import { Runtime_Client_StorageSchema } from '@dxos/protocols/buf/dxos/config_pb';
|
|
13
|
+
import { type Client } from '@dxos/react-client';
|
|
14
|
+
|
|
15
|
+
import { translations } from '#translations';
|
|
16
|
+
|
|
17
|
+
const setupDevtools = () => {
|
|
18
|
+
(globalThis as any).composer ??= {};
|
|
19
|
+
|
|
20
|
+
// Used to test how composer handles breaking protocol changes.
|
|
21
|
+
(globalThis as any).composer.changeStorageVersionInMetadata = async (version: number) => {
|
|
22
|
+
const { changeStorageVersionInMetadata } = await import('@dxos/client-services/testing');
|
|
23
|
+
const { createStorageObjects } = await import('@dxos/client-services');
|
|
24
|
+
const client: Client = (window as any).dxos.client;
|
|
25
|
+
const config = client.config;
|
|
26
|
+
await client.destroy();
|
|
27
|
+
const { storage } = createStorageObjects(
|
|
28
|
+
config.values?.runtime?.client?.storage ?? create(Runtime_Client_StorageSchema, {}),
|
|
29
|
+
);
|
|
30
|
+
await changeStorageVersionInMetadata(storage, version);
|
|
31
|
+
location.pathname = '/';
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const SetupDevtools = Capability.inlineModule('setup-devtools', { provides: [] }, () =>
|
|
36
|
+
Effect.sync(() => setupDevtools()),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export const Translations = AppCapability.translations(translations);
|
|
40
|
+
|
|
41
|
+
export const AppGraphBuilder = undefined;
|
|
42
|
+
export const PluginAsset = undefined;
|
|
43
|
+
export const ReactContext = undefined;
|
|
44
|
+
export const ReactSurface = undefined;
|
|
@@ -2,8 +2,54 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { create } from '@bufbuild/protobuf';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
import * as Capability from '@dxos/app-framework/Capability';
|
|
9
|
+
import * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';
|
|
10
|
+
import * as AppCapability from '@dxos/app-toolkit/AppCapability';
|
|
11
|
+
import { Runtime_Client_StorageSchema } from '@dxos/protocols/buf/dxos/config_pb';
|
|
12
|
+
import { type Client } from '@dxos/react-client';
|
|
13
|
+
|
|
14
|
+
import { meta } from '#meta';
|
|
15
|
+
import { translations } from '#translations';
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line import/no-relative-packages
|
|
18
|
+
import pluginSpec from '../../PLUGIN.mdl?raw';
|
|
19
|
+
|
|
20
|
+
export const AppGraphBuilder = AppCapability.appGraphBuilder(() => import('./app-graph-builder'), {
|
|
21
|
+
requires: [AppCapabilities.AppGraph],
|
|
22
|
+
environments: ['node'],
|
|
23
|
+
});
|
|
24
|
+
export const ReactContext = AppCapability.reactContext(() => import('./react-context'));
|
|
25
|
+
export const ReactSurface = AppCapability.surface(() => import('./react-surface'), {
|
|
26
|
+
roles: ['org.dxos.role.article', 'org.dxos.role.deckCompanion.devtoolsOverview'],
|
|
27
|
+
});
|
|
28
|
+
export const SetupDevtools = Capability.inlineModule('setup-devtools', { provides: [] }, () =>
|
|
29
|
+
Effect.sync(() => setupDevtools()),
|
|
30
|
+
);
|
|
31
|
+
export const Translations = AppCapability.translations(translations);
|
|
32
|
+
export const PluginAsset = AppCapability.pluginAsset({
|
|
33
|
+
pluginId: meta.profile.key,
|
|
34
|
+
path: 'PLUGIN.mdl',
|
|
35
|
+
content: pluginSpec,
|
|
36
|
+
mimeType: 'application/x-mdl',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const setupDevtools = () => {
|
|
40
|
+
(globalThis as any).composer ??= {};
|
|
41
|
+
|
|
42
|
+
// Used to test how composer handles breaking protocol changes.
|
|
43
|
+
(globalThis as any).composer.changeStorageVersionInMetadata = async (version: number) => {
|
|
44
|
+
const { changeStorageVersionInMetadata } = await import('@dxos/client-services/testing');
|
|
45
|
+
const { createStorageObjects } = await import('@dxos/client-services');
|
|
46
|
+
const client: Client = (window as any).dxos.client;
|
|
47
|
+
const config = client.config;
|
|
48
|
+
await client.destroy();
|
|
49
|
+
const { storage } = createStorageObjects(
|
|
50
|
+
config.values?.runtime?.client?.storage ?? create(Runtime_Client_StorageSchema, {}),
|
|
51
|
+
);
|
|
52
|
+
await changeStorageVersionInMetadata(storage, version);
|
|
53
|
+
location.pathname = '/';
|
|
54
|
+
};
|
|
55
|
+
};
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import * as Capabilities from '@dxos/app-framework/Capabilities';
|
|
9
|
+
import * as Capability from '@dxos/app-framework/Capability';
|
|
9
10
|
import { Surface } from '@dxos/app-framework/ui';
|
|
10
11
|
import { DevtoolsContextProvider } from '@dxos/devtools';
|
|
11
12
|
|
|
@@ -13,7 +14,7 @@ import { meta } from '#meta';
|
|
|
13
14
|
|
|
14
15
|
export default Capability.makeModule(() =>
|
|
15
16
|
Effect.succeed(
|
|
16
|
-
Capability.
|
|
17
|
+
Capability.contribute(Capabilities.ReactContext, {
|
|
17
18
|
id: meta.profile.key,
|
|
18
19
|
context: ({ children }) => (
|
|
19
20
|
<Surface.ProfilerProvider>
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import * as Capabilities from '@dxos/app-framework/Capabilities';
|
|
8
|
+
import * as Capability from '@dxos/app-framework/Capability';
|
|
8
9
|
import { Surface } from '@dxos/app-framework/ui';
|
|
10
|
+
import type * as AppGraph from '@dxos/app-graph/AppGraph';
|
|
9
11
|
import { AppSurface } from '@dxos/app-toolkit/ui';
|
|
10
12
|
import { EDGE_SERVICE_DEFAULTS, EdgeServiceName } from '@dxos/config';
|
|
11
13
|
import {
|
|
@@ -31,10 +33,9 @@ import {
|
|
|
31
33
|
SwarmPanel,
|
|
32
34
|
WorkflowPanel,
|
|
33
35
|
} from '@dxos/devtools';
|
|
34
|
-
import { type Graph } from '@dxos/plugin-graph';
|
|
35
36
|
import { ToolsExplorer } from '@dxos/react-ui-introspect';
|
|
36
37
|
|
|
37
|
-
import { DebugGraph, DevtoolsOverviewContainer, RegistryPanel } from '#containers';
|
|
38
|
+
import { CliPanel, DebugGraph, DevtoolsOverviewContainer, RegistryPanel } from '#containers';
|
|
38
39
|
import { Devtools } from '#types';
|
|
39
40
|
|
|
40
41
|
import {
|
|
@@ -48,7 +49,7 @@ import {
|
|
|
48
49
|
const MCP_SERVER_URL = EDGE_SERVICE_DEFAULTS[EdgeServiceName.Introspect];
|
|
49
50
|
|
|
50
51
|
type GraphDebug = {
|
|
51
|
-
graph:
|
|
52
|
+
graph: AppGraph.Graph;
|
|
52
53
|
root: string;
|
|
53
54
|
};
|
|
54
55
|
|
|
@@ -69,7 +70,7 @@ const isGraphDebug = (data: unknown): data is GraphDebug => {
|
|
|
69
70
|
|
|
70
71
|
export default Capability.makeModule(
|
|
71
72
|
Effect.fnUntraced(function* () {
|
|
72
|
-
return Capability.
|
|
73
|
+
return Capability.contribute(Capabilities.ReactSurface, [
|
|
73
74
|
Surface.create({
|
|
74
75
|
id: 'appGraph',
|
|
75
76
|
filter: AppSurface.subject(AppSurface.Article, isGraphDebug),
|
|
@@ -82,6 +83,11 @@ export default Capability.makeModule(
|
|
|
82
83
|
component: ToolsExplorer,
|
|
83
84
|
props: () => ({ serverUrl: MCP_SERVER_URL }),
|
|
84
85
|
}),
|
|
86
|
+
Surface.create({
|
|
87
|
+
id: 'cli',
|
|
88
|
+
filter: AppSurface.literal(AppSurface.Article, Devtools.Cli),
|
|
89
|
+
component: CliPanel,
|
|
90
|
+
}),
|
|
85
91
|
Surface.create({
|
|
86
92
|
id: 'registry',
|
|
87
93
|
filter: AppSurface.literal(AppSurface.Article, Devtools.Echo.Registry),
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import { userEvent } from 'storybook/test';
|
|
8
|
+
|
|
9
|
+
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
10
|
+
import { ClientPlugin, initializeIdentity } from '@dxos/plugin-client/testing';
|
|
11
|
+
import * as SpacePlugin from '@dxos/plugin-space/SpacePlugin';
|
|
12
|
+
import { corePlugins } from '@dxos/plugin-testing';
|
|
13
|
+
import * as StorybookPlugin from '@dxos/plugin-testing/StorybookPlugin';
|
|
14
|
+
import { runCommand, waitForTerminal } from '@dxos/react-ui-terminal/testing';
|
|
15
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
16
|
+
|
|
17
|
+
import { translations } from '#translations';
|
|
18
|
+
|
|
19
|
+
import { CliPanel } from './CliPanel';
|
|
20
|
+
|
|
21
|
+
const meta = {
|
|
22
|
+
title: 'plugins/plugin-devtools/containers/CliPanel',
|
|
23
|
+
component: CliPanel,
|
|
24
|
+
decorators: [
|
|
25
|
+
withLayout({ layout: 'fullscreen' }),
|
|
26
|
+
withTheme(),
|
|
27
|
+
withPluginManager({
|
|
28
|
+
// No setup event: the panel fires `CommandsRequested` itself, which is what pulls the
|
|
29
|
+
// plugins' command modules in — the same path the app takes.
|
|
30
|
+
plugins: [
|
|
31
|
+
...corePlugins(),
|
|
32
|
+
// The identity brings a default space, which is what the commands resolve against.
|
|
33
|
+
ClientPlugin.make({ onClientInitialized: ({ client }) => Effect.asVoid(initializeIdentity(client)) }),
|
|
34
|
+
StorybookPlugin.make({}),
|
|
35
|
+
// The real plugin, so the story covers it actually contributing its commands.
|
|
36
|
+
SpacePlugin.make({}),
|
|
37
|
+
],
|
|
38
|
+
}),
|
|
39
|
+
],
|
|
40
|
+
parameters: {
|
|
41
|
+
layout: 'fullscreen',
|
|
42
|
+
controls: { disable: true },
|
|
43
|
+
translations,
|
|
44
|
+
},
|
|
45
|
+
} satisfies Meta<typeof CliPanel>;
|
|
46
|
+
|
|
47
|
+
export default meta;
|
|
48
|
+
|
|
49
|
+
type Story = StoryObj<typeof meta>;
|
|
50
|
+
|
|
51
|
+
export const Default: Story = {};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Drives real `dx` commands against the story's client, so a break in the wiring between the shell
|
|
55
|
+
* and the client surfaces here rather than in the app.
|
|
56
|
+
*/
|
|
57
|
+
export const Spec: Story = {
|
|
58
|
+
play: async ({ canvasElement }) => {
|
|
59
|
+
const keyboard = (text: string) => userEvent.keyboard(text);
|
|
60
|
+
await waitForTerminal(canvasElement, 'dx>');
|
|
61
|
+
|
|
62
|
+
// `space list` reaches the client and reports its space.
|
|
63
|
+
await runCommand(canvasElement, 'space list', keyboard);
|
|
64
|
+
await waitForTerminal(canvasElement, 'SPACE_READY');
|
|
65
|
+
|
|
66
|
+
// `database query` reaches ECHO through the layer the command provides for itself.
|
|
67
|
+
await runCommand(canvasElement, 'database query', keyboard);
|
|
68
|
+
await waitForTerminal(canvasElement, 'org.dxos.type.spaceProperties');
|
|
69
|
+
|
|
70
|
+
// A command from another plugin, whose service arrives via that plugin's contributed layer
|
|
71
|
+
// rather than anything this panel names.
|
|
72
|
+
await runCommand(canvasElement, 'config view', keyboard);
|
|
73
|
+
await waitForTerminal(canvasElement, 'runtime');
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { Placeholder } from '@dxos/devtools';
|
|
8
|
+
import { useClient } from '@dxos/react-client';
|
|
9
|
+
import { Panel } from '@dxos/react-ui';
|
|
10
|
+
import { Terminal } from '@dxos/react-ui-terminal';
|
|
11
|
+
|
|
12
|
+
import { useCliApp } from './useCliApp';
|
|
13
|
+
|
|
14
|
+
const BOLD = '\x1b[1m';
|
|
15
|
+
const DIM = '\x1b[2m';
|
|
16
|
+
const RESET = '\x1b[0m';
|
|
17
|
+
|
|
18
|
+
const BANNER = `${BOLD}DXOS CLI${RESET}\n${DIM}The dx commands, running against this client. Type "help" for the list.${RESET}`;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Runs the `dx` CLI against the client backing this app.
|
|
22
|
+
*
|
|
23
|
+
* The command tree is assembled from the commands the installed plugins contribute, and it and its
|
|
24
|
+
* services are built once per client, so state the handlers hold survives between commands the way
|
|
25
|
+
* it does in a long-lived shell.
|
|
26
|
+
*/
|
|
27
|
+
export const CliPanel = () => {
|
|
28
|
+
const client = useClient();
|
|
29
|
+
const cli = useCliApp(client);
|
|
30
|
+
|
|
31
|
+
// No ScrollArea: xterm owns its own viewport and scrollback.
|
|
32
|
+
return (
|
|
33
|
+
<Panel.Root>
|
|
34
|
+
<Panel.Content>
|
|
35
|
+
{cli ? (
|
|
36
|
+
<Terminal command={cli.command} layer={cli.layer} name='dx' banner={BANNER} />
|
|
37
|
+
) : (
|
|
38
|
+
<Placeholder label='No commands' />
|
|
39
|
+
)}
|
|
40
|
+
</Panel.Content>
|
|
41
|
+
</Panel.Root>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
CliPanel.displayName = 'CliPanel';
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Layer from 'effect/Layer';
|
|
7
|
+
import * as Command from 'effect/unstable/cli/Command';
|
|
8
|
+
import * as Options from 'effect/unstable/cli/Flag';
|
|
9
|
+
import { useEffect, useMemo } from 'react';
|
|
10
|
+
|
|
11
|
+
import * as ActivationEvents from '@dxos/app-framework/ActivationEvents';
|
|
12
|
+
import * as Capabilities from '@dxos/app-framework/Capabilities';
|
|
13
|
+
import * as Capability from '@dxos/app-framework/Capability';
|
|
14
|
+
import * as Plugin from '@dxos/app-framework/Plugin';
|
|
15
|
+
import { useCapabilities, usePluginManager } from '@dxos/app-framework/ui';
|
|
16
|
+
import { CommandConfig, type CommandServices } from '@dxos/cli-util';
|
|
17
|
+
import { type Client, ClientService, ConfigService } from '@dxos/client';
|
|
18
|
+
import * as Operation from '@dxos/compute/Operation';
|
|
19
|
+
import { EffectEx } from '@dxos/effect';
|
|
20
|
+
|
|
21
|
+
const OPERATIONS_UNAVAILABLE = 'Operations are not available in the devtools terminal.';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Commands that create objects invoke operations, which this surface has no runtime for. Failing
|
|
25
|
+
* with a stated reason beats the bare "Service not found" a missing layer would produce.
|
|
26
|
+
*/
|
|
27
|
+
const operationLayer = Layer.succeed(Operation.Service, {
|
|
28
|
+
invoke: () => Effect.die(OPERATIONS_UNAVAILABLE),
|
|
29
|
+
schedule: () => Effect.die(OPERATIONS_UNAVAILABLE),
|
|
30
|
+
invokePromise: async () => ({ error: new Error(OPERATIONS_UNAVAILABLE) }),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Builds the `dx` command tree and the services its handlers need.
|
|
35
|
+
*
|
|
36
|
+
* Mounting is the demand signal: command modules are gated on `CommandsRequested`, which nothing
|
|
37
|
+
* else in the app fires, so a plugin's commands stay off the critical path until a terminal opens.
|
|
38
|
+
* The capability is read through its atom, so commands appear as they activate and enabling or
|
|
39
|
+
* disabling a plugin later rebuilds the tree rather than leaving the terminal on the set that
|
|
40
|
+
* existed when it mounted.
|
|
41
|
+
*
|
|
42
|
+
* The root mirrors the binary's, minus the options that only mean something on a filesystem
|
|
43
|
+
* (`--config`, `--profile`); everything else resolves the same way, including `--space-id`
|
|
44
|
+
* defaulting to the first available space.
|
|
45
|
+
*
|
|
46
|
+
* Returns `undefined` until something contributes commands — there is no shell to offer yet.
|
|
47
|
+
*/
|
|
48
|
+
export const useCliApp = (client: Client) => {
|
|
49
|
+
const manager = usePluginManager();
|
|
50
|
+
const commands = useCapabilities(Capabilities.Command);
|
|
51
|
+
const contributed = useCapabilities(Capabilities.Layer);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
EffectEx.runDetached(manager.activate(ActivationEvents.CommandsRequested));
|
|
55
|
+
}, [manager]);
|
|
56
|
+
|
|
57
|
+
return useMemo(() => {
|
|
58
|
+
// Destructured rather than passed as an array so the non-empty shape `withSubcommands` requires
|
|
59
|
+
// is carried by the type instead of asserted.
|
|
60
|
+
const [first, ...rest] = commands;
|
|
61
|
+
if (!first) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const command = Command.make('dx').pipe(
|
|
66
|
+
// Shared so they parse anywhere in the line — a plain command config is only read when `dx`
|
|
67
|
+
// itself is the leaf.
|
|
68
|
+
Command.withSharedFlags({
|
|
69
|
+
json: Options.boolean('json').pipe(Options.withDescription('JSON output.')),
|
|
70
|
+
verbose: Options.boolean('verbose').pipe(Options.withAlias('v'), Options.withDescription('Verbose output.')),
|
|
71
|
+
}),
|
|
72
|
+
Command.withSubcommands([first, ...rest]),
|
|
73
|
+
// After the subcommands so the config reaches them: `provide` wraps the handler it is applied
|
|
74
|
+
// to, and `withSubcommands` replaces that handler with one that dispatches to the children.
|
|
75
|
+
// The input widens to a union across the tree, hence the presence checks.
|
|
76
|
+
Command.provide((input) =>
|
|
77
|
+
Layer.succeed(CommandConfig, {
|
|
78
|
+
json: 'json' in input ? input.json : false,
|
|
79
|
+
verbose: 'verbose' in input ? input.verbose : false,
|
|
80
|
+
profile: 'default',
|
|
81
|
+
logLevel: 'info',
|
|
82
|
+
}),
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Annotated so a service this host owes is a build error naming it, rather than a
|
|
87
|
+
// "Service not found" the first time someone runs the command that needed it.
|
|
88
|
+
const hostLayer: Layer.Layer<CommandServices, never, never> = Layer.mergeAll(
|
|
89
|
+
ClientService.fromClient(client),
|
|
90
|
+
ConfigService.fromConfig(client.config),
|
|
91
|
+
Layer.succeed(Plugin.Service, manager),
|
|
92
|
+
Layer.succeed(Capability.Service, manager.capabilities),
|
|
93
|
+
operationLayer,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Plugins carry anything further their own commands need. A contributed layer may fail to
|
|
97
|
+
// build, and the shell runs on a fiber with nowhere to report that, so it dies here instead
|
|
98
|
+
// of being smuggled into the terminal as an unhandled error channel.
|
|
99
|
+
const layer = Layer.orDie(Layer.mergeAll(hostLayer, ...contributed));
|
|
100
|
+
|
|
101
|
+
return { command, layer };
|
|
102
|
+
}, [client, manager, commands, contributed]);
|
|
103
|
+
};
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
+
import * as AppGraph from '@dxos/app-graph/AppGraph';
|
|
7
8
|
import { Tree } from '@dxos/devtools';
|
|
8
|
-
import { Graph } from '@dxos/plugin-graph';
|
|
9
9
|
import { Panel, ScrollArea } from '@dxos/react-ui';
|
|
10
10
|
|
|
11
|
-
export type DebugGraphProps = { graph:
|
|
11
|
+
export type DebugGraphProps = { graph: AppGraph.Graph; root: string };
|
|
12
12
|
|
|
13
13
|
export const DebugGraph = ({ graph, root }: DebugGraphProps) => {
|
|
14
14
|
return (
|
|
@@ -16,7 +16,7 @@ export const DebugGraph = ({ graph, root }: DebugGraphProps) => {
|
|
|
16
16
|
<Panel.Content asChild>
|
|
17
17
|
<ScrollArea.Root orientation='all'>
|
|
18
18
|
<ScrollArea.Viewport>
|
|
19
|
-
<Tree data={
|
|
19
|
+
<Tree data={AppGraph.toJSON(graph, root)} />
|
|
20
20
|
</ScrollArea.Viewport>
|
|
21
21
|
</ScrollArea.Root>
|
|
22
22
|
</Panel.Content>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { type ReactNode, createContext, useContext, useEffect, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { IconButton, ScrollArea, useTranslation } from '@dxos/react-ui';
|
|
7
|
+
import { Flex, IconButton, ScrollArea, useTranslation } from '@dxos/react-ui';
|
|
8
8
|
|
|
9
9
|
import { meta } from '#meta';
|
|
10
10
|
|
|
@@ -136,20 +136,18 @@ const formatRelative = (iso: string): string => {
|
|
|
136
136
|
|
|
137
137
|
const PullRow = ({ pull }: { pull: GithubPullRequest }) => (
|
|
138
138
|
<li>
|
|
139
|
-
<
|
|
140
|
-
href={pull.html_url}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
</div>
|
|
152
|
-
</a>
|
|
139
|
+
<Flex asChild align='start' gap='sm' classNames='px-trim-sm py-trim-xs rounded-sm hover:bg-hover-surface'>
|
|
140
|
+
<a href={pull.html_url} target='_blank' rel='noopener noreferrer'>
|
|
141
|
+
<img src={pull.user.avatar_url} alt='' className='size-6 rounded-full shrink-0' />
|
|
142
|
+
<Flex column classNames='min-w-0 flex-1'>
|
|
143
|
+
{/* `leading-6` gives the title the avatar's line box, so the two align on their own. */}
|
|
144
|
+
<span className='text-sm leading-6 truncate'>{pull.title}</span>
|
|
145
|
+
<span className='text-xs text-description truncate'>
|
|
146
|
+
#{pull.number} · {pull.user.login} · {pull.merged_at ? formatRelative(pull.merged_at) : ''}
|
|
147
|
+
</span>
|
|
148
|
+
</Flex>
|
|
149
|
+
</a>
|
|
150
|
+
</Flex>
|
|
153
151
|
</li>
|
|
154
152
|
);
|
|
155
153
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import * as Operation from '@dxos/compute/Operation';
|
|
8
8
|
import { JsonView, Placeholder, Searchbar } from '@dxos/devtools';
|
|
9
9
|
import { Entity, Format, Obj, Type } from '@dxos/echo';
|
|
10
10
|
import { useClient } from '@dxos/react-client';
|
package/src/containers/index.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { type ComponentType, lazy } from 'react';
|
|
6
6
|
|
|
7
|
+
export const CliPanel: ComponentType<any> = lazy(() => import('./CliPanel'));
|
|
7
8
|
export const DebugGraph: ComponentType<any> = lazy(() => import('./DebugGraph'));
|
|
8
9
|
export const DevtoolsOverviewContainer: ComponentType<any> = lazy(() => import('./DevtoolsOverviewContainer'));
|
|
9
10
|
export const GithubPanel: ComponentType<any> = lazy(() => import('./GithubPanel'));
|
package/src/index.ts
CHANGED
package/src/meta.ts
CHANGED
|
@@ -6,19 +6,20 @@ import { describe, test } from 'vitest';
|
|
|
6
6
|
|
|
7
7
|
import { createComposerTestApp } from '@dxos/plugin-testing/harness';
|
|
8
8
|
|
|
9
|
+
import { meta } from '#meta';
|
|
9
10
|
import { DevtoolsPlugin } from '#plugin';
|
|
10
11
|
|
|
11
|
-
import { meta } from './meta';
|
|
12
|
-
|
|
13
12
|
const moduleId = (name: string) => `${meta.profile.key}.module.${name}`;
|
|
14
13
|
|
|
15
14
|
describe('DevtoolsPlugin', () => {
|
|
16
|
-
|
|
15
|
+
// Boot imports the app-graph-builder body (the harness fires the plugin's start event), which
|
|
16
|
+
// can exceed the default 15s under vite-node transform load.
|
|
17
|
+
test('modules activate on the expected events', { timeout: 60_000 }, async ({ expect }) => {
|
|
17
18
|
await using harness = await createComposerTestApp({
|
|
18
19
|
plugins: [DevtoolsPlugin()],
|
|
19
20
|
});
|
|
20
21
|
|
|
21
|
-
// AppGraphBuilder
|
|
22
|
+
// AppGraphBuilder rides the plugin's start event, fired by the harness after Startup.
|
|
22
23
|
expect(harness.manager.getActive()).toContain(moduleId('AppGraphBuilder'));
|
|
23
24
|
});
|
|
24
25
|
});
|
package/src/plugin.tsx
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Plugin from '@dxos/app-framework/Plugin';
|
|
6
|
+
|
|
7
|
+
import { AppGraphBuilder, PluginAsset, ReactContext, ReactSurface, SetupDevtools, Translations } from '#capabilities';
|
|
8
|
+
import { meta } from '#meta';
|
|
9
|
+
|
|
10
|
+
export const DevtoolsPlugin = Plugin.define(meta).pipe(
|
|
11
|
+
Plugin.addModule(AppGraphBuilder),
|
|
12
|
+
Plugin.addModule(PluginAsset),
|
|
13
|
+
Plugin.addModule(ReactContext),
|
|
14
|
+
Plugin.addModule(ReactSurface),
|
|
15
|
+
Plugin.addModule(SetupDevtools),
|
|
16
|
+
Plugin.addModule(Translations),
|
|
17
|
+
Plugin.make,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export default DevtoolsPlugin;
|
package/src/translations.ts
CHANGED
|
@@ -18,6 +18,7 @@ export const translations = [
|
|
|
18
18
|
'devtools-overview.label': 'Stats',
|
|
19
19
|
'debug-app-graph.label': 'App Graph',
|
|
20
20
|
'debug-tools-explorer.label': 'SDK Explorer',
|
|
21
|
+
'cli.label': 'CLI',
|
|
21
22
|
|
|
22
23
|
'reset-data.label': 'Reset data (ERASES ALL DATA)',
|
|
23
24
|
'client.label': 'Client',
|
package/src/types/Devtools.ts
CHANGED
|
@@ -18,6 +18,7 @@ const devtoolsId = `${meta.profile.key}.devtools`;
|
|
|
18
18
|
export const id = devtoolsId;
|
|
19
19
|
export const AppGraph = `${devtoolsId}.appGraph`;
|
|
20
20
|
export const ToolsExplorer = `${devtoolsId}.toolsExplorer`;
|
|
21
|
+
export const Cli = `${devtoolsId}.cli`;
|
|
21
22
|
|
|
22
23
|
export namespace Client {
|
|
23
24
|
export const id = `${devtoolsId}.client`;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// @import-as-namespace
|
|
6
|
+
|
|
7
|
+
import * as ActivationEvent from '@dxos/app-framework/ActivationEvent';
|
|
8
|
+
|
|
9
|
+
import { meta } from '#meta';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The feature's start event: the plugin's start-gated modules (and cross-plugin
|
|
13
|
+
* contributions consumed by this feature) activate here. Fired on demand and by the
|
|
14
|
+
* host's idle trickle.
|
|
15
|
+
*/
|
|
16
|
+
export const Start = ActivationEvent.pluginStart(meta.profile.key);
|
package/src/types/index.ts
CHANGED