@dxos/plugin-registry 0.8.4-staging.60fe92afc8 → 0.9.1-main.c7dcc2e112

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.
Files changed (124) hide show
  1. package/dist/lib/neutral/RegistryPlugin.node.mjs +709 -18
  2. package/dist/lib/neutral/RegistryPlugin.node.mjs.map +4 -4
  3. package/dist/lib/neutral/chunk-ZY37XLVF.mjs +46 -0
  4. package/dist/lib/neutral/chunk-ZY37XLVF.mjs.map +7 -0
  5. package/dist/lib/neutral/index.mjs +1 -1
  6. package/dist/lib/neutral/meta.json +1 -1
  7. package/dist/lib/neutral/{open-plugin-registry-II735N76.mjs → open-plugin-registry-ID7LV3SF.mjs} +3 -3
  8. package/dist/lib/neutral/open-plugin-registry-ID7LV3SF.mjs.map +7 -0
  9. package/dist/lib/neutral/plugin.mjs +2 -2
  10. package/dist/lib/neutral/translations.mjs +1 -1
  11. package/dist/lib/neutral/translations.mjs.map +3 -3
  12. package/dist/types/dx.config.d.ts +28 -0
  13. package/dist/types/dx.config.d.ts.map +1 -0
  14. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  15. package/dist/types/src/capabilities/index.d.ts +4 -12
  16. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  17. package/dist/types/src/capabilities/react-surface.d.ts +2 -2
  18. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  19. package/dist/types/src/categories.d.ts.map +1 -1
  20. package/dist/types/src/commands/index.d.ts +2 -2
  21. package/dist/types/src/commands/index.d.ts.map +1 -1
  22. package/dist/types/src/commands/plugin/disable.d.ts.map +1 -0
  23. package/dist/types/src/commands/plugin/enable.d.ts.map +1 -0
  24. package/dist/types/src/commands/plugin/index.d.ts +3 -0
  25. package/dist/types/src/commands/plugin/index.d.ts.map +1 -0
  26. package/dist/types/src/commands/plugin/list.d.ts.map +1 -0
  27. package/dist/types/src/commands/plugin/util.d.ts.map +1 -0
  28. package/dist/types/src/commands/registry/index.d.ts +16 -0
  29. package/dist/types/src/commands/registry/index.d.ts.map +1 -0
  30. package/dist/types/src/commands/registry/publish-package.d.ts +29 -0
  31. package/dist/types/src/commands/registry/publish-package.d.ts.map +1 -0
  32. package/dist/types/src/commands/registry/publish-publisher.d.ts +19 -0
  33. package/dist/types/src/commands/registry/publish-publisher.d.ts.map +1 -0
  34. package/dist/types/src/commands/registry/publish.d.ts +25 -0
  35. package/dist/types/src/commands/registry/publish.d.ts.map +1 -0
  36. package/dist/types/src/commands/registry/records.d.ts +14 -0
  37. package/dist/types/src/commands/registry/records.d.ts.map +1 -0
  38. package/dist/types/src/commands/registry/unpublish.d.ts +15 -0
  39. package/dist/types/src/commands/registry/unpublish.d.ts.map +1 -0
  40. package/dist/types/src/commands/registry/util.d.ts +113 -0
  41. package/dist/types/src/commands/registry/util.d.ts.map +1 -0
  42. package/dist/types/src/commands/registry/verify.d.ts +20 -0
  43. package/dist/types/src/commands/registry/verify.d.ts.map +1 -0
  44. package/dist/types/src/components/PluginDetail/PluginDetail.d.ts +2 -2
  45. package/dist/types/src/components/PluginDetail/PluginDetail.d.ts.map +1 -1
  46. package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts.map +1 -1
  47. package/dist/types/src/components/PluginList/PluginItem.d.ts.map +1 -1
  48. package/dist/types/src/components/PluginList/PluginList.stories.d.ts.map +1 -1
  49. package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts.map +1 -1
  50. package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts.map +1 -1
  51. package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts.map +1 -1
  52. package/dist/types/src/hooks/useAutoTags.d.ts +2 -2
  53. package/dist/types/src/hooks/useAutoTags.d.ts.map +1 -1
  54. package/dist/types/src/hooks/useCatalogEntry.d.ts +4 -5
  55. package/dist/types/src/hooks/useCatalogEntry.d.ts.map +1 -1
  56. package/dist/types/src/hooks/usePluginActions.d.ts +3 -3
  57. package/dist/types/src/hooks/usePluginActions.d.ts.map +1 -1
  58. package/dist/types/src/hooks/useUpdateAvailable.d.ts +2 -2
  59. package/dist/types/src/hooks/useUpdateAvailable.d.ts.map +1 -1
  60. package/dist/types/src/hooks/useVersionPicker.d.ts +7 -8
  61. package/dist/types/src/hooks/useVersionPicker.d.ts.map +1 -1
  62. package/dist/types/src/meta.d.ts +28 -2
  63. package/dist/types/src/meta.d.ts.map +1 -1
  64. package/dist/types/src/operations/open-plugin-registry.d.ts.map +1 -1
  65. package/dist/types/src/types.d.ts.map +1 -1
  66. package/dist/types/tsconfig.tsbuildinfo +1 -1
  67. package/dx.config.ts +20 -0
  68. package/package.json +31 -24
  69. package/src/RegistryPlugin.node.ts +2 -2
  70. package/src/RegistryPlugin.test.ts +1 -1
  71. package/src/capabilities/app-graph-builder.ts +43 -39
  72. package/src/capabilities/dev-plugin-loader.ts +1 -1
  73. package/src/capabilities/react-surface.tsx +1 -4
  74. package/src/capabilities/settings.ts +2 -2
  75. package/src/categories.ts +5 -4
  76. package/src/commands/index.ts +2 -10
  77. package/src/commands/{disable.ts → plugin/disable.ts} +3 -3
  78. package/src/commands/{enable.ts → plugin/enable.ts} +3 -3
  79. package/src/commands/plugin/index.ts +14 -0
  80. package/src/commands/{list.ts → plugin/list.ts} +4 -4
  81. package/src/commands/registry/index.ts +30 -0
  82. package/src/commands/registry/publish-package.ts +119 -0
  83. package/src/commands/registry/publish-publisher.ts +73 -0
  84. package/src/commands/registry/publish.ts +278 -0
  85. package/src/commands/registry/records.ts +73 -0
  86. package/src/commands/registry/unpublish.ts +70 -0
  87. package/src/commands/registry/util.ts +395 -0
  88. package/src/commands/registry/verify.ts +76 -0
  89. package/src/components/DisableDependentsAlert/DisableDependentsAlert.tsx +1 -1
  90. package/src/components/PluginDetail/PluginDetail.stories.tsx +5 -3
  91. package/src/components/PluginDetail/PluginDetail.tsx +21 -23
  92. package/src/components/PluginFailureBadge/PluginFailureBadge.tsx +1 -1
  93. package/src/components/PluginList/PluginItem.tsx +5 -3
  94. package/src/components/PluginList/PluginList.stories.tsx +1 -2
  95. package/src/components/PluginList/PluginList.tsx +4 -4
  96. package/src/components/RegistrySettings/RegistrySettings.tsx +1 -1
  97. package/src/containers/BaseRegistryArticle/BaseRegistryArticle.tsx +2 -2
  98. package/src/containers/LoadPluginDialog/LoadPluginDialog.tsx +3 -3
  99. package/src/containers/PluginArticle/PluginArticle.tsx +8 -6
  100. package/src/containers/PublicRegistryArticle/PublicRegistryArticle.tsx +35 -28
  101. package/src/containers/RegistryArticle/RegistryArticle.tsx +1 -1
  102. package/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.tsx +1 -1
  103. package/src/hooks/useAutoTags.ts +3 -3
  104. package/src/hooks/useCatalogEntry.ts +6 -7
  105. package/src/hooks/useDisableConfirmation.ts +1 -1
  106. package/src/hooks/usePluginActions.ts +13 -11
  107. package/src/hooks/useUpdateAvailable.ts +5 -5
  108. package/src/hooks/useVersionPicker.ts +22 -30
  109. package/src/meta.ts +6 -15
  110. package/src/operations/open-plugin-registry.ts +2 -2
  111. package/src/translations.ts +1 -1
  112. package/src/types.ts +3 -1
  113. package/dist/lib/neutral/chunk-GRQI6MLI.mjs +0 -36
  114. package/dist/lib/neutral/chunk-GRQI6MLI.mjs.map +0 -7
  115. package/dist/lib/neutral/open-plugin-registry-II735N76.mjs.map +0 -7
  116. package/dist/types/src/commands/disable.d.ts.map +0 -1
  117. package/dist/types/src/commands/enable.d.ts.map +0 -1
  118. package/dist/types/src/commands/list.d.ts.map +0 -1
  119. package/dist/types/src/commands/util.d.ts.map +0 -1
  120. /package/dist/types/src/commands/{disable.d.ts → plugin/disable.d.ts} +0 -0
  121. /package/dist/types/src/commands/{enable.d.ts → plugin/enable.d.ts} +0 -0
  122. /package/dist/types/src/commands/{list.d.ts → plugin/list.d.ts} +0 -0
  123. /package/dist/types/src/commands/{util.d.ts → plugin/util.d.ts} +0 -0
  124. /package/src/commands/{util.ts → plugin/util.ts} +0 -0
package/dx.config.ts ADDED
@@ -0,0 +1,20 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Config2 } from '@dxos/app-framework/config';
6
+ import { trim } from '@dxos/util';
7
+
8
+ export default Config2.make({
9
+ plugin: {
10
+ key: 'org.dxos.plugin.registry',
11
+ name: 'Plugins',
12
+ author: 'DXOS',
13
+ description: trim`
14
+ Plugin management system for discovering, installing, and configuring workspace extensions.
15
+ Browse available plugins and customize your workspace capabilities.
16
+ `,
17
+ icon: { key: 'ph--squares-four--regular' },
18
+ tags: ['system'],
19
+ },
20
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-registry",
3
- "version": "0.8.4-staging.60fe92afc8",
3
+ "version": "0.9.1-main.c7dcc2e112",
4
4
  "description": "Braneframe plugin gallery",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -52,28 +52,35 @@
52
52
  "types": "dist/types/src/index.d.ts",
53
53
  "files": [
54
54
  "dist",
55
+ "dx.config.ts",
55
56
  "src"
56
57
  ],
57
58
  "dependencies": {
58
59
  "@effect-atom/atom-react": "^0.5.0",
59
60
  "@effect/cli": "0.75.2",
60
61
  "yaml": "^2.8.2",
61
- "@dxos/app-toolkit": "0.8.4-staging.60fe92afc8",
62
- "@dxos/app-framework": "0.8.4-staging.60fe92afc8",
63
- "@dxos/cli-util": "0.8.4-staging.60fe92afc8",
64
- "@dxos/client-protocol": "0.8.4-staging.60fe92afc8",
65
- "@dxos/compute": "0.8.4-staging.60fe92afc8",
66
- "@dxos/effect": "0.8.4-staging.60fe92afc8",
67
- "@dxos/log": "0.8.4-staging.60fe92afc8",
68
- "@dxos/plugin-graph": "0.8.4-staging.60fe92afc8",
69
- "@dxos/react-ui-form": "0.8.4-staging.60fe92afc8",
70
- "@dxos/keys": "0.8.4-staging.60fe92afc8",
71
- "@dxos/invariant": "0.8.4-staging.60fe92afc8",
72
- "@dxos/plugin-observability": "0.8.4-staging.60fe92afc8",
73
- "@dxos/react-ui-mosaic": "0.8.4-staging.60fe92afc8",
74
- "@dxos/react-ui-markdown": "0.8.4-staging.60fe92afc8",
75
- "@dxos/react-ui-stack": "0.8.4-staging.60fe92afc8",
76
- "@dxos/util": "0.8.4-staging.60fe92afc8"
62
+ "@dxos/app-framework": "0.9.1-main.c7dcc2e112",
63
+ "@dxos/app-toolkit": "0.9.1-main.c7dcc2e112",
64
+ "@dxos/client": "0.9.1-main.c7dcc2e112",
65
+ "@dxos/client-protocol": "0.9.1-main.c7dcc2e112",
66
+ "@dxos/cli-util": "0.9.1-main.c7dcc2e112",
67
+ "@dxos/compute": "0.9.1-main.c7dcc2e112",
68
+ "@dxos/edge-client": "0.9.1-main.c7dcc2e112",
69
+ "@dxos/invariant": "0.9.1-main.c7dcc2e112",
70
+ "@dxos/echo": "0.9.1-main.c7dcc2e112",
71
+ "@dxos/keys": "0.9.1-main.c7dcc2e112",
72
+ "@dxos/plugin-graph": "0.9.1-main.c7dcc2e112",
73
+ "@dxos/log": "0.9.1-main.c7dcc2e112",
74
+ "@dxos/protocols": "0.9.1-main.c7dcc2e112",
75
+ "@dxos/context": "0.9.1-main.c7dcc2e112",
76
+ "@dxos/plugin-observability": "0.9.1-main.c7dcc2e112",
77
+ "@dxos/react-ui-form": "0.9.1-main.c7dcc2e112",
78
+ "@dxos/react-ui-mosaic": "0.9.1-main.c7dcc2e112",
79
+ "@dxos/react-ui-stack": "0.9.1-main.c7dcc2e112",
80
+ "@dxos/types": "0.9.1-main.c7dcc2e112",
81
+ "@dxos/react-ui-markdown": "0.9.1-main.c7dcc2e112",
82
+ "@dxos/util": "0.9.1-main.c7dcc2e112",
83
+ "@dxos/effect": "0.9.1-main.c7dcc2e112"
77
84
  },
78
85
  "devDependencies": {
79
86
  "@effect-atom/atom-react": "^0.5.0",
@@ -84,11 +91,11 @@
84
91
  "react": "~19.2.3",
85
92
  "react-dom": "~19.2.3",
86
93
  "vite": "^8.0.16",
87
- "@dxos/plugin-testing": "0.8.4-staging.60fe92afc8",
88
- "@dxos/random": "0.8.4-staging.60fe92afc8",
89
- "@dxos/react-ui": "0.8.4-staging.60fe92afc8",
90
- "@dxos/ui-theme": "0.8.4-staging.60fe92afc8",
91
- "@dxos/storybook-utils": "0.8.4-staging.60fe92afc8"
94
+ "@dxos/plugin-testing": "0.9.1-main.c7dcc2e112",
95
+ "@dxos/react-ui": "0.9.1-main.c7dcc2e112",
96
+ "@dxos/storybook-utils": "0.9.1-main.c7dcc2e112",
97
+ "@dxos/ui-theme": "0.9.1-main.c7dcc2e112",
98
+ "@dxos/random": "0.9.1-main.c7dcc2e112"
92
99
  },
93
100
  "peerDependencies": {
94
101
  "@effect-atom/atom-react": "^0.5.0",
@@ -96,8 +103,8 @@
96
103
  "effect": "3.21.3",
97
104
  "react": "~19.2.3",
98
105
  "react-dom": "~19.2.3",
99
- "@dxos/react-ui": "0.8.4-staging.60fe92afc8",
100
- "@dxos/ui-theme": "0.8.4-staging.60fe92afc8"
106
+ "@dxos/react-ui": "0.9.1-main.c7dcc2e112",
107
+ "@dxos/ui-theme": "0.9.1-main.c7dcc2e112"
101
108
  },
102
109
  "publishConfig": {
103
110
  "access": "public"
@@ -7,11 +7,11 @@ import { AppPlugin } from '@dxos/app-toolkit';
7
7
 
8
8
  import { meta } from '#meta';
9
9
 
10
- import { plugin } from './commands';
10
+ import { plugin, registry } from './commands';
11
11
 
12
12
  export const RegistryPlugin = Plugin.define(meta).pipe(
13
13
  AppPlugin.addCommandModule({
14
- commands: [plugin],
14
+ commands: [plugin, registry],
15
15
  }),
16
16
  Plugin.make,
17
17
  );
@@ -10,7 +10,7 @@ import { RegistryPlugin } from '#plugin';
10
10
 
11
11
  import { meta } from './meta';
12
12
 
13
- const moduleId = (name: string) => `${meta.id}.module.${name}`;
13
+ const moduleId = (name: string) => `${meta.profile.key}.module.${name}`;
14
14
 
15
15
  describe('RegistryPlugin', () => {
16
16
  test('modules activate on the expected events', async ({ expect }) => {
@@ -4,7 +4,7 @@
4
4
 
5
5
  import * as Effect from 'effect/Effect';
6
6
 
7
- import { Capabilities, Capability, type Registry, Plugin } from '@dxos/app-framework';
7
+ import { Capabilities, Capability, Plugin } from '@dxos/app-framework';
8
8
  import { AppCapabilities, LayoutOperation, SettingsOperation } from '@dxos/app-toolkit';
9
9
  import { Operation } from '@dxos/compute';
10
10
  import { DXN } from '@dxos/keys';
@@ -21,19 +21,19 @@ import { LOAD_PLUGIN_DIALOG } from '../containers';
21
21
  * modules and only exists so the article surface can render details for
22
22
  * registry plugins that haven't been installed yet.
23
23
  */
24
- const toDisplayPlugin = (entry: Registry.Plugin): Plugin.Plugin =>
24
+ const toDisplayPlugin = (entry: Plugin.Meta): Plugin.Plugin =>
25
25
  ({
26
26
  [Plugin.PluginTypeId]: Plugin.PluginTypeId,
27
27
  meta: Plugin.makeMeta({
28
- key: DXN.make(entry.id),
29
- name: entry.name,
30
- description: entry.description,
31
- homePage: entry.homePage,
32
- source: entry.source,
33
- screenshots: entry.screenshots,
34
- tags: entry.tags,
35
- icon: entry.icon,
36
- iconHue: entry.iconHue,
28
+ key: DXN.make(entry.profile.key),
29
+ name: entry.profile.name,
30
+ description: entry.profile.description,
31
+ homePage: entry.profile.homePage,
32
+ source: entry.profile.source,
33
+ screenshots: entry.profile.screenshots,
34
+ tags: entry.profile.tags,
35
+ icon: entry.profile.icon,
36
+ author: entry.profile.author,
37
37
  }),
38
38
  modules: [],
39
39
  }) as Plugin.Plugin;
@@ -52,7 +52,7 @@ export default Capability.makeModule(
52
52
  id: 'openRegistry',
53
53
  data: () => Operation.invoke(SettingsOperation.OpenPluginRegistry),
54
54
  properties: {
55
- label: ['open-plugin-registry.label', { ns: meta.id }],
55
+ label: ['open-plugin-registry.label', { ns: meta.profile.key }],
56
56
  icon: 'ph--squares-four--regular',
57
57
  disposition: 'menu',
58
58
  },
@@ -77,9 +77,9 @@ export default Capability.makeModule(
77
77
  return Effect.succeed([
78
78
  Node.make({
79
79
  id: REGISTRY_ID,
80
- type: meta.id,
80
+ type: meta.profile.key,
81
81
  properties: {
82
- label: ['plugin-registry.label', { ns: meta.id }],
82
+ label: ['plugin-registry.label', { ns: meta.profile.key }],
83
83
  icon: 'ph--squares-four--regular',
84
84
  disposition: 'pin-end',
85
85
  position: 'first',
@@ -91,7 +91,7 @@ export default Capability.makeModule(
91
91
  type: 'category',
92
92
  data: registryCategoryId('bundled'),
93
93
  properties: {
94
- label: ['bundled-plugins.label', { ns: meta.id }],
94
+ label: ['bundled-plugins.label', { ns: meta.profile.key }],
95
95
  icon: 'ph--squares-four--regular',
96
96
  key: REGISTRY_KEY,
97
97
  testId: 'pluginRegistry.bundled',
@@ -103,7 +103,7 @@ export default Capability.makeModule(
103
103
  type: 'category',
104
104
  data: registryCategoryId('installed'),
105
105
  properties: {
106
- label: ['installed-plugins.label', { ns: meta.id }],
106
+ label: ['installed-plugins.label', { ns: meta.profile.key }],
107
107
  icon: 'ph--check--regular',
108
108
  key: REGISTRY_KEY,
109
109
  testId: 'pluginRegistry.installed',
@@ -115,7 +115,7 @@ export default Capability.makeModule(
115
115
  type: 'category',
116
116
  data: registryCategoryId('recommended'),
117
117
  properties: {
118
- label: ['recommended-plugins.label', { ns: meta.id }],
118
+ label: ['recommended-plugins.label', { ns: meta.profile.key }],
119
119
  icon: 'ph--star--regular',
120
120
  key: REGISTRY_KEY,
121
121
  testId: 'pluginRegistry.recommended',
@@ -127,25 +127,29 @@ export default Capability.makeModule(
127
127
  type: 'category',
128
128
  data: registryCategoryId('labs'),
129
129
  properties: {
130
- label: ['labs-plugins.label', { ns: meta.id }],
130
+ label: ['labs-plugins.label', { ns: meta.profile.key }],
131
131
  icon: 'ph--flask--regular',
132
132
  key: REGISTRY_KEY,
133
133
  testId: 'pluginRegistry.labs',
134
134
  count: categoryCount(registryCategoryId('labs')),
135
135
  },
136
136
  }),
137
- Node.make({
138
- id: registryCategoryId('registry'),
139
- type: 'category',
140
- data: registryCategoryId('registry'),
141
- properties: {
142
- label: ['registry-plugins.label', { ns: meta.id }],
143
- icon: 'ph--users-three--regular',
144
- key: REGISTRY_KEY,
145
- testId: 'pluginRegistry.registry',
146
- count: registryCount,
147
- },
148
- }),
137
+ ...(registryCount > 0
138
+ ? [
139
+ Node.make({
140
+ id: registryCategoryId('registry'),
141
+ type: 'category',
142
+ data: registryCategoryId('registry'),
143
+ properties: {
144
+ label: ['registry-plugins.label', { ns: meta.profile.key }],
145
+ icon: 'ph--users-three--regular',
146
+ key: REGISTRY_KEY,
147
+ testId: 'pluginRegistry.registry',
148
+ count: registryCount,
149
+ },
150
+ }),
151
+ ]
152
+ : []),
149
153
  ],
150
154
  }),
151
155
  ]);
@@ -165,7 +169,7 @@ export default Capability.makeModule(
165
169
  });
166
170
  }),
167
171
  properties: {
168
- label: ['load-by-url.label', { ns: meta.id }],
172
+ label: ['load-by-url.label', { ns: meta.profile.key }],
169
173
  icon: 'ph--cloud-arrow-down--regular',
170
174
  disposition: 'list-item-primary',
171
175
  },
@@ -177,16 +181,16 @@ export default Capability.makeModule(
177
181
  match: NodeMatcher.whenId(`root/${REGISTRY_ID}`),
178
182
  connector: (_node, get) => {
179
183
  const manager = capabilities.get(Capabilities.PluginManager);
180
- const installedIds = new Set(manager.getPlugins().map((plugin) => plugin.meta.id));
184
+ const installedIds = new Set(manager.getPlugins().map((plugin) => plugin.meta.profile.key));
181
185
 
182
186
  const installedNodes = manager.getPlugins().map((plugin) =>
183
187
  Node.make({
184
- id: plugin.meta.id,
188
+ id: plugin.meta.profile.key,
185
189
  type: 'org.dxos.plugin',
186
190
  data: plugin,
187
191
  properties: {
188
- label: plugin.meta.name ?? plugin.meta.id,
189
- icon: plugin.meta.icon ?? 'ph--circle--regular',
192
+ label: plugin.meta.profile.name ?? plugin.meta.profile.key,
193
+ icon: plugin.meta.profile.icon?.key ?? 'ph--circle--regular',
190
194
  disposition: 'hidden',
191
195
  },
192
196
  }),
@@ -194,16 +198,16 @@ export default Capability.makeModule(
194
198
 
195
199
  const registryEntries = get(manager.pluginRegistry.plugins).entries;
196
200
  const registryNodes = registryEntries
197
- .filter((entry) => !installedIds.has(DXN.make(entry.id)))
201
+ .filter((entry) => !installedIds.has(DXN.make(entry.profile.key)))
198
202
  .map((entry) => {
199
203
  const plugin = toDisplayPlugin(entry);
200
204
  return Node.make({
201
- id: plugin.meta.id,
205
+ id: plugin.meta.profile.key,
202
206
  type: 'org.dxos.plugin',
203
207
  data: plugin,
204
208
  properties: {
205
- label: plugin.meta.name ?? plugin.meta.id,
206
- icon: plugin.meta.icon ?? 'ph--circle--regular',
209
+ label: plugin.meta.profile.name ?? plugin.meta.profile.key,
210
+ icon: plugin.meta.profile.icon?.key ?? 'ph--circle--regular',
207
211
  disposition: 'hidden',
208
212
  },
209
213
  });
@@ -29,7 +29,7 @@ export default Capability.makeModule(
29
29
 
30
30
  log.info('loading dev plugin from registry settings', { url });
31
31
  yield* manager.add(url).pipe(
32
- Effect.flatMap((plugin) => manager.enable(plugin.meta.id)),
32
+ Effect.flatMap((plugin) => manager.enable(plugin.meta.profile.key)),
33
33
  Effect.tapError((error) => Effect.sync(() => log.warn('dev plugin auto-load failed', { url, error }))),
34
34
  Effect.ignore,
35
35
  );
@@ -7,7 +7,6 @@ import React, { type ComponentProps, useMemo } from 'react';
7
7
 
8
8
  import { Capabilities, Capability, Plugin } from '@dxos/app-framework';
9
9
  import { Surface, usePluginManager } from '@dxos/app-framework/ui';
10
- import { AppCapabilities } from '@dxos/app-toolkit';
11
10
  import { AppSurface } from '@dxos/app-toolkit/ui';
12
11
 
13
12
  import { DisableDependentsAlert } from '#components';
@@ -86,9 +85,7 @@ export default Capability.makeModule(() =>
86
85
  }),
87
86
  Surface.create({
88
87
  id: 'pluginSettings',
89
- role: 'article',
90
- filter: (data): data is { subject: AppCapabilities.Settings } =>
91
- AppCapabilities.isSettings(data.subject) && data.subject.prefix === meta.id,
88
+ filter: AppSurface.settings(AppSurface.Article, meta.profile.key),
92
89
  component: ({ data: { subject } }) => <RegistrySettingsContainer subject={subject} />,
93
90
  }),
94
91
  ]),
@@ -17,7 +17,7 @@ const DEFAULT_DEV_PLUGIN_URL = `http://localhost:${PLUGIN_DEV_SERVER_PORT}/manif
17
17
  export default Capability.makeModule(() =>
18
18
  Effect.sync(() => {
19
19
  const settingsAtom = createKvsStore({
20
- key: meta.id,
20
+ key: meta.profile.key,
21
21
  schema: RegistrySettingsSchema,
22
22
  defaultValue: () => ({ devPluginUrl: DEFAULT_DEV_PLUGIN_URL }),
23
23
  });
@@ -25,7 +25,7 @@ export default Capability.makeModule(() =>
25
25
  return [
26
26
  Capability.contributes(RegistryCapabilities.Settings, settingsAtom),
27
27
  Capability.contributes(AppCapabilities.Settings, {
28
- prefix: meta.id,
28
+ prefix: meta.profile.key,
29
29
  schema: RegistrySettingsSchema,
30
30
  atom: settingsAtom,
31
31
  }),
package/src/categories.ts CHANGED
@@ -27,13 +27,14 @@ export const getCategoryPredicate = (
27
27
  ): PluginPredicate => {
28
28
  switch (category) {
29
29
  case registryCategoryId('bundled'):
30
- return ({ meta }) => !core.includes(meta.id) && !remoteIds.has(meta.id);
30
+ return ({ meta }) => !core.includes(meta.profile.key) && !remoteIds.has(meta.profile.key);
31
31
  case registryCategoryId('installed'):
32
- return ({ meta }) => !core.includes(meta.id) && enabled.includes(meta.id);
32
+ return ({ meta }) => !core.includes(meta.profile.key) && enabled.includes(meta.profile.key);
33
33
  case registryCategoryId('recommended'):
34
- return ({ meta }) => !core.includes(meta.id) && !remoteIds.has(meta.id) && !meta.tags?.includes('labs');
34
+ return ({ meta }) =>
35
+ !core.includes(meta.profile.key) && !remoteIds.has(meta.profile.key) && !meta.profile.tags?.includes('labs');
35
36
  case registryCategoryId('labs'):
36
- return ({ meta }) => meta.tags?.includes('labs') ?? false;
37
+ return ({ meta }) => meta.profile.tags?.includes('labs') ?? false;
37
38
  default:
38
39
  return () => false;
39
40
  }
@@ -2,13 +2,5 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import * as Command from '@effect/cli/Command';
6
-
7
- import { disable } from './disable';
8
- import { enable } from './enable';
9
- import { list } from './list';
10
-
11
- export const plugin: Command.Command<any, any, any, any> = Command.make('plugin').pipe(
12
- Command.withDescription('Manage plugins.'),
13
- Command.withSubcommands([enable, disable, list]),
14
- );
5
+ export * from './plugin';
6
+ export * from './registry';
@@ -11,14 +11,14 @@ import { Plugin } from '@dxos/app-framework';
11
11
  import { CommandConfig } from '@dxos/cli-util';
12
12
  import { invariant } from '@dxos/invariant';
13
13
 
14
- import { saveEnabledPlugins } from '../storage';
14
+ import { saveEnabledPlugins } from '../../storage';
15
15
 
16
16
  export const handler = Effect.fn(function* ({ id }: { id: string }) {
17
17
  const { json, profile } = yield* CommandConfig;
18
18
  const manager = yield* Plugin.Service;
19
19
 
20
20
  const plugins = manager.getPlugins();
21
- const plugin = plugins.find((p: Plugin.Plugin) => p.meta.id === id);
21
+ const plugin = plugins.find((p: Plugin.Plugin) => p.meta.profile.key === id);
22
22
  invariant(plugin, `Plugin not found: ${id}`);
23
23
 
24
24
  const core = manager.getCore();
@@ -34,7 +34,7 @@ export const handler = Effect.fn(function* ({ id }: { id: string }) {
34
34
  if (json) {
35
35
  yield* Console.log(JSON.stringify({ id, enabled: false }, null, 2));
36
36
  } else {
37
- yield* Console.log(`Plugin "${plugin.meta.name ?? id}" disabled.`);
37
+ yield* Console.log(`Plugin "${plugin.meta.profile.name ?? id}" disabled.`);
38
38
  }
39
39
  });
40
40
 
@@ -11,14 +11,14 @@ import { Plugin } from '@dxos/app-framework';
11
11
  import { CommandConfig } from '@dxos/cli-util';
12
12
  import { invariant } from '@dxos/invariant';
13
13
 
14
- import { saveEnabledPlugins } from '../storage';
14
+ import { saveEnabledPlugins } from '../../storage';
15
15
 
16
16
  export const handler = Effect.fn(function* ({ id }: { id: string }) {
17
17
  const { json, profile } = yield* CommandConfig;
18
18
  const manager = yield* Plugin.Service;
19
19
 
20
20
  const plugins = manager.getPlugins();
21
- const plugin = plugins.find((p: Plugin.Plugin) => p.meta.id === id);
21
+ const plugin = plugins.find((p: Plugin.Plugin) => p.meta.profile.key === id);
22
22
  invariant(plugin, `Plugin not found: ${id}`);
23
23
 
24
24
  const enabled = yield* manager.enable(id);
@@ -31,7 +31,7 @@ export const handler = Effect.fn(function* ({ id }: { id: string }) {
31
31
  if (json) {
32
32
  yield* Console.log(JSON.stringify({ id, enabled: true }, null, 2));
33
33
  } else {
34
- yield* Console.log(`Plugin "${plugin.meta.name ?? id}" enabled.`);
34
+ yield* Console.log(`Plugin "${plugin.meta.profile.name ?? id}" enabled.`);
35
35
  }
36
36
  });
37
37
 
@@ -0,0 +1,14 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Command from '@effect/cli/Command';
6
+
7
+ import { disable } from './disable';
8
+ import { enable } from './enable';
9
+ import { list } from './list';
10
+
11
+ export const plugin: Command.Command<any, any, any, any> = Command.make('plugin').pipe(
12
+ Command.withDescription('Manage plugins.'),
13
+ Command.withSubcommands([enable, disable, list]),
14
+ );
@@ -20,11 +20,11 @@ export const handler = Effect.fn(function* () {
20
20
  const core = manager.getCore();
21
21
 
22
22
  const formattedPlugins: FormattedPlugin[] = plugins.map((plugin: Plugin.Plugin) => {
23
- const isEnabled = enabled.includes(plugin.meta.id);
24
- const isCore = core.includes(plugin.meta.id);
23
+ const isEnabled = enabled.includes(plugin.meta.profile.key);
24
+ const isCore = core.includes(plugin.meta.profile.key);
25
25
  return {
26
- id: plugin.meta.id,
27
- name: plugin.meta.name ?? plugin.meta.id,
26
+ id: plugin.meta.profile.key,
27
+ name: plugin.meta.profile.name ?? plugin.meta.profile.key,
28
28
  enabled: isEnabled,
29
29
  core: isCore,
30
30
  };
@@ -0,0 +1,30 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Command from '@effect/cli/Command';
6
+
7
+ import { publish } from './publish';
8
+ import { publishPackage } from './publish-package';
9
+ import { publishPublisher } from './publish-publisher';
10
+ import { records } from './records';
11
+ import { unpublish } from './unpublish';
12
+ import { verify } from './verify';
13
+
14
+ /**
15
+ * `dx registry *` — commands for publishing and inspecting records that the
16
+ * DXOS plugin registry indexes from AT Protocol. Auth uses an App Password
17
+ * (per-user secret minted at https://bsky.app/settings/app-passwords); OAuth
18
+ * support is a follow-up.
19
+ *
20
+ * - `publish` — config-driven: build from dx.yml, host the bundle, write records.
21
+ * - `publish-package` — low-level: write profile + release records from flags.
22
+ * - `publish-publisher` — write the publisher's own profile record.
23
+ * - `unpublish` — remove a package (profile + releases) from your PDS.
24
+ * - `verify` — write a publisher.verification record (indexed only from the configured verifier).
25
+ * - `records` — list everything the authenticated repo has published under `org.dxos.experimental.*`.
26
+ */
27
+ export const registry: Command.Command<any, any, any, any> = Command.make('registry').pipe(
28
+ Command.withDescription('Publish to and inspect the AT Protocol-backed DXOS plugin registry.'),
29
+ Command.withSubcommands([publish, publishPackage, publishPublisher, unpublish, verify, records]),
30
+ );
@@ -0,0 +1,119 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Command from '@effect/cli/Command';
6
+ import * as Options from '@effect/cli/Options';
7
+ import * as FetchHttpClient from '@effect/platform/FetchHttpClient';
8
+ import * as Console from 'effect/Console';
9
+ import * as Effect from 'effect/Effect';
10
+ import * as Function from 'effect/Function';
11
+ import * as Option from 'effect/Option';
12
+
13
+ import { ClientService } from '@dxos/client';
14
+ import { type PluginProfile, type PluginRelease } from '@dxos/protocols';
15
+
16
+ import { AUTH_OPTION_DESCRIPTIONS, NSID, putRecord, resolveSession } from './util';
17
+
18
+ /**
19
+ * `dx registry publish-package` — publishes both the mutable
20
+ * `package.profile` (rkey = key) and a `package.release`
21
+ * (rkey = `<key>:<version>`) record to the authenticated user's PDS.
22
+ *
23
+ * The two writes are independent `putRecord` (upsert) calls; if the second fails the
24
+ * first is left in place (the indexer treats a profile with no releases as
25
+ * present-but-unpublishable, so a half-success is benign). Releases are conceptually
26
+ * single-write per version but not enforced — see docs/registry-spec.md (Integrity).
27
+ */
28
+ export const publishPackage = Command.make(
29
+ 'publish-package',
30
+ {
31
+ handle: Options.text('handle').pipe(Options.withDescription(AUTH_OPTION_DESCRIPTIONS.handle), Options.optional),
32
+ appPassword: Options.text('app-password').pipe(
33
+ Options.withDescription(AUTH_OPTION_DESCRIPTIONS.appPassword),
34
+ Options.optional,
35
+ ),
36
+ key: Options.text('key').pipe(
37
+ Options.withDescription(
38
+ 'Plugin key — a reverse-domain NSID (e.g. org.dxos.plugin.excalidraw); also the profile record rkey.',
39
+ ),
40
+ ),
41
+ name: Options.text('name').pipe(Options.withDescription('Human-readable package name.')),
42
+ description: Options.text('description').pipe(
43
+ Options.withDescription('Short description shown in the registry.'),
44
+ Options.withDefault(''),
45
+ ),
46
+ version: Options.text('version').pipe(
47
+ Options.withDescription('Semver version (no build metadata). The release rkey is `<key>:<version>`.'),
48
+ ),
49
+ moduleUrl: Options.text('module-url').pipe(
50
+ Options.withDescription('HTTPS URL to the bundle or manifest for this release.'),
51
+ ),
52
+ homepage: Options.text('homepage').pipe(Options.withDescription('Homepage URL.'), Options.optional),
53
+ source: Options.text('source').pipe(Options.withDescription('Source repository URL.'), Options.optional),
54
+ tag: Options.text('tag').pipe(
55
+ Options.withDescription('Tag (repeatable). Categorizes the package for discovery.'),
56
+ Options.repeated,
57
+ ),
58
+ iconKey: Options.text('icon-key').pipe(
59
+ Options.withDescription('Icon identifier (e.g. ph--sparkle--regular) resolvable by @ch-ui/icons.'),
60
+ Options.optional,
61
+ ),
62
+ iconHue: Options.text('icon-hue').pipe(Options.withDescription('Icon palette hue (e.g. blue).'), Options.optional),
63
+ manifestHash: Options.text('manifest-hash').pipe(
64
+ Options.withDescription('Optional content hash for the bundle/manifest.'),
65
+ Options.optional,
66
+ ),
67
+ },
68
+ (options) =>
69
+ Function.pipe(
70
+ Effect.gen(function* () {
71
+ const client = yield* ClientService;
72
+ const session = yield* resolveSession({
73
+ handle: Option.getOrUndefined(options.handle),
74
+ appPassword: Option.getOrUndefined(options.appPassword),
75
+ client,
76
+ });
77
+ const createdAt = new Date().toISOString();
78
+
79
+ const homepage = Option.getOrUndefined(options.homepage);
80
+ const source = Option.getOrUndefined(options.source);
81
+ const iconKey = Option.getOrUndefined(options.iconKey);
82
+ const iconHue = Option.getOrUndefined(options.iconHue);
83
+ const icon =
84
+ iconKey !== undefined ? { key: iconKey, ...(iconHue !== undefined && { hue: iconHue }) } : undefined;
85
+
86
+ const profile: PluginProfile & { createdAt: string } = {
87
+ key: options.key,
88
+ name: options.name,
89
+ ...(options.description.length > 0 && { description: options.description }),
90
+ ...(homepage !== undefined && { homePage: homepage }),
91
+ ...(source !== undefined && { source }),
92
+ ...(options.tag.length > 0 && { tags: options.tag }),
93
+ ...(icon !== undefined && { icon }),
94
+ createdAt,
95
+ };
96
+
97
+ const profileResult = yield* putRecord(session, NSID.PackageProfile, options.key, profile);
98
+ yield* Console.log(`Profile ${profileResult.uri}`);
99
+
100
+ const manifestHash = Option.getOrUndefined(options.manifestHash);
101
+ const release: PluginRelease & { package: string; createdAt: string } = {
102
+ package: options.key,
103
+ version: options.version,
104
+ moduleUrl: options.moduleUrl,
105
+ createdAt,
106
+ ...(manifestHash !== undefined && { manifestHash }),
107
+ };
108
+
109
+ const releaseResult = yield* putRecord(
110
+ session,
111
+ NSID.PackageRelease,
112
+ `${options.key}:${options.version}`,
113
+ release,
114
+ );
115
+ yield* Console.log(`Release ${releaseResult.uri}`);
116
+ }),
117
+ Effect.provide(FetchHttpClient.layer),
118
+ ),
119
+ ).pipe(Command.withDescription("Publish a package profile + release to the authenticated user's PDS."));