@dxos/plugin-script 0.7.5-labs.ea4b4c2 → 0.7.5-labs.f400bbc
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/browser/ScriptContainer-3RFMYKTV.mjs +258 -0
- package/dist/lib/browser/ScriptContainer-3RFMYKTV.mjs.map +7 -0
- package/dist/lib/browser/{artifact-UOESRBEZ.mjs → artifact-definition-4LWPD3DH.mjs} +27 -19
- package/dist/lib/browser/artifact-definition-4LWPD3DH.mjs.map +7 -0
- package/dist/lib/browser/{chunk-45LXDWNF.mjs → chunk-5SXOHNAK.mjs} +3 -3
- package/dist/lib/browser/chunk-5SXOHNAK.mjs.map +7 -0
- package/dist/lib/browser/{ScriptContainer-AL4ZLJID.mjs → chunk-G2FYNDU4.mjs} +19 -255
- package/dist/lib/browser/chunk-G2FYNDU4.mjs.map +7 -0
- package/dist/lib/browser/chunk-PZPYF7M4.mjs +69 -0
- package/dist/lib/browser/chunk-PZPYF7M4.mjs.map +7 -0
- package/dist/lib/browser/{chunk-ALQHMUJN.mjs → chunk-VTWBVFBK.mjs} +2 -4
- package/dist/lib/browser/{chunk-ALQHMUJN.mjs.map → chunk-VTWBVFBK.mjs.map} +3 -3
- package/dist/lib/browser/index.mjs +60 -29
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/{intent-resolver-CMKQTLBG.mjs → intent-resolver-D6FPY5ZA.mjs} +2 -2
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-YELZNYPR.mjs → react-surface-G4C2PZ2K.mjs} +23 -7
- package/dist/lib/browser/react-surface-G4C2PZ2K.mjs.map +7 -0
- package/dist/lib/node/ScriptContainer-G6FZU4AM.cjs +274 -0
- package/dist/lib/node/ScriptContainer-G6FZU4AM.cjs.map +7 -0
- package/dist/lib/node/{artifact-PDTB4LV4.cjs → artifact-definition-BXQCFPAH.cjs} +26 -21
- package/dist/lib/node/artifact-definition-BXQCFPAH.cjs.map +7 -0
- package/dist/lib/node/chunk-ITAJ3WRW.cjs +77 -0
- package/dist/lib/node/chunk-ITAJ3WRW.cjs.map +7 -0
- package/dist/lib/node/{chunk-IID7WUQS.cjs → chunk-K3ZZWVXS.cjs} +5 -5
- package/dist/lib/node/chunk-K3ZZWVXS.cjs.map +7 -0
- package/dist/lib/node/{chunk-F5Z3DMHA.cjs → chunk-TOXBGIEI.cjs} +5 -8
- package/dist/lib/node/{chunk-F5Z3DMHA.cjs.map → chunk-TOXBGIEI.cjs.map} +3 -3
- package/dist/lib/node/{ScriptContainer-FKCNWHBW.cjs → chunk-XKYJYY3T.cjs} +62 -281
- package/dist/lib/node/chunk-XKYJYY3T.cjs.map +7 -0
- package/dist/lib/node/index.cjs +58 -30
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/{intent-resolver-V5ASU5DW.cjs → intent-resolver-NYJFPD5B.cjs} +6 -6
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/{react-surface-SMMFAPZD.cjs → react-surface-EGSE3FMN.cjs} +24 -12
- package/dist/lib/node/react-surface-EGSE3FMN.cjs.map +7 -0
- package/dist/lib/node-esm/ScriptContainer-7FTCFIF3.mjs +259 -0
- package/dist/lib/node-esm/ScriptContainer-7FTCFIF3.mjs.map +7 -0
- package/dist/lib/node-esm/{artifact-IX4ZCTUF.mjs → artifact-definition-KT4DK2LQ.mjs} +26 -19
- package/dist/lib/node-esm/artifact-definition-KT4DK2LQ.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-CGF7EOGD.mjs → chunk-2AFXX34U.mjs} +3 -3
- package/dist/lib/node-esm/chunk-2AFXX34U.mjs.map +7 -0
- package/dist/lib/node-esm/{ScriptContainer-52KVAFUY.mjs → chunk-NRICF4KE.mjs} +19 -255
- package/dist/lib/node-esm/chunk-NRICF4KE.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-GXRS4YIV.mjs → chunk-S64WSJQK.mjs} +2 -4
- package/dist/lib/node-esm/{chunk-GXRS4YIV.mjs.map → chunk-S64WSJQK.mjs.map} +3 -3
- package/dist/lib/node-esm/chunk-YIWRJ25J.mjs +71 -0
- package/dist/lib/node-esm/chunk-YIWRJ25J.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +60 -29
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/{intent-resolver-75BHFRUK.mjs → intent-resolver-KLBHSMW4.mjs} +2 -2
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{react-surface-7PHSWVOU.mjs → react-surface-7YSGHZQM.mjs} +23 -7
- package/dist/lib/node-esm/react-surface-7YSGHZQM.mjs.map +7 -0
- package/dist/types/src/ScriptPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/{artifact.d.ts → artifact-definition.d.ts} +1 -1
- package/dist/types/src/capabilities/artifact-definition.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +1 -1
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/components/DebugPanel/DebugPanel.d.ts +1 -2
- package/dist/types/src/components/DebugPanel/DebugPanel.d.ts.map +1 -1
- package/dist/types/src/components/FrameContainer/FrameContainer.d.ts +3 -4
- package/dist/types/src/components/FrameContainer/FrameContainer.d.ts.map +1 -1
- package/dist/types/src/components/ScriptContainer.d.ts +1 -2
- package/dist/types/src/components/ScriptContainer.d.ts.map +1 -1
- package/dist/types/src/components/ScriptSettings/ScriptSettings.d.ts +1 -2
- package/dist/types/src/components/ScriptSettings/ScriptSettings.d.ts.map +1 -1
- package/dist/types/src/components/ScriptSettingsPanel.d.ts +1 -2
- package/dist/types/src/components/ScriptSettingsPanel.d.ts.map +1 -1
- package/dist/types/src/components/ScriptToolbar/ScriptToolbar.d.ts +1 -2
- package/dist/types/src/components/ScriptToolbar/ScriptToolbar.d.ts.map +1 -1
- package/dist/types/src/components/TypescriptEditor/TypescriptEditor.d.ts +1 -2
- package/dist/types/src/components/TypescriptEditor/TypescriptEditor.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/events.d.ts +4 -0
- package/dist/types/src/events.d.ts.map +1 -0
- package/dist/types/src/hooks/deploy.d.ts.map +1 -1
- package/dist/types/src/hooks/template.d.ts +1 -1
- package/dist/types/src/hooks/view.d.ts +1 -1
- package/dist/types/src/hooks/view.d.ts.map +1 -1
- package/dist/types/src/templates/chess.d.ts +1 -4
- package/dist/types/src/templates/chess.d.ts.map +1 -1
- package/dist/types/src/templates/discord.d.ts +3 -0
- package/dist/types/src/templates/discord.d.ts.map +1 -0
- package/dist/types/src/templates/forex-effect.d.ts +0 -3
- package/dist/types/src/templates/forex-effect.d.ts.map +1 -1
- package/dist/types/src/templates/forex.d.ts +0 -3
- package/dist/types/src/templates/forex.d.ts.map +1 -1
- package/dist/types/src/templates/index.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +3 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +1 -1
- package/package.json +43 -43
- package/src/ScriptPlugin.tsx +38 -18
- package/src/capabilities/{artifact.ts → artifact-definition.ts} +15 -10
- package/src/capabilities/index.ts +1 -1
- package/src/capabilities/react-surface.tsx +9 -3
- package/src/components/DebugPanel/DebugPanel.tsx +2 -2
- package/src/components/FrameContainer/FrameContainer.tsx +5 -3
- package/src/components/TypescriptEditor/TypescriptEditor.tsx +2 -2
- package/src/components/index.ts +1 -1
- package/src/events.ts +11 -0
- package/src/hooks/deploy.ts +2 -2
- package/src/hooks/view.ts +4 -1
- package/src/templates/chat.ts +4 -4
- package/src/templates/chess.ts +42 -35
- package/src/templates/discord.ts +103 -0
- package/src/templates/forex-effect.ts +16 -17
- package/src/templates/forex.ts +2 -3
- package/src/templates/index.ts +10 -2
- package/src/translations.ts +3 -0
- package/dist/lib/browser/AutomationPanel-J3SZ5L3Y.mjs +0 -78
- package/dist/lib/browser/AutomationPanel-J3SZ5L3Y.mjs.map +0 -7
- package/dist/lib/browser/ScriptContainer-AL4ZLJID.mjs.map +0 -7
- package/dist/lib/browser/artifact-UOESRBEZ.mjs.map +0 -7
- package/dist/lib/browser/chunk-45LXDWNF.mjs.map +0 -7
- package/dist/lib/browser/chunk-VIX22OMB.mjs +0 -60
- package/dist/lib/browser/chunk-VIX22OMB.mjs.map +0 -7
- package/dist/lib/browser/react-surface-YELZNYPR.mjs.map +0 -7
- package/dist/lib/node/AutomationPanel-4M55FURO.cjs +0 -104
- package/dist/lib/node/AutomationPanel-4M55FURO.cjs.map +0 -7
- package/dist/lib/node/ScriptContainer-FKCNWHBW.cjs.map +0 -7
- package/dist/lib/node/artifact-PDTB4LV4.cjs.map +0 -7
- package/dist/lib/node/chunk-IID7WUQS.cjs.map +0 -7
- package/dist/lib/node/chunk-PKT5DD3O.cjs +0 -70
- package/dist/lib/node/chunk-PKT5DD3O.cjs.map +0 -7
- package/dist/lib/node/react-surface-SMMFAPZD.cjs.map +0 -7
- package/dist/lib/node-esm/AutomationPanel-W6SKG35R.mjs +0 -79
- package/dist/lib/node-esm/AutomationPanel-W6SKG35R.mjs.map +0 -7
- package/dist/lib/node-esm/ScriptContainer-52KVAFUY.mjs.map +0 -7
- package/dist/lib/node-esm/artifact-IX4ZCTUF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CGF7EOGD.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-NBAL5DXN.mjs +0 -62
- package/dist/lib/node-esm/chunk-NBAL5DXN.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-7PHSWVOU.mjs.map +0 -7
- package/dist/types/src/capabilities/artifact.d.ts.map +0 -1
- package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts +0 -17
- package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts.map +0 -1
- package/dist/types/src/components/AutomationPanel/index.d.ts +0 -4
- package/dist/types/src/components/AutomationPanel/index.d.ts.map +0 -1
- package/src/components/AutomationPanel/AutomationPanel.tsx +0 -104
- package/src/components/AutomationPanel/index.ts +0 -9
- /package/dist/lib/browser/{intent-resolver-CMKQTLBG.mjs.map → intent-resolver-D6FPY5ZA.mjs.map} +0 -0
- /package/dist/lib/node/{intent-resolver-V5ASU5DW.cjs.map → intent-resolver-NYJFPD5B.cjs.map} +0 -0
- /package/dist/lib/node-esm/{intent-resolver-75BHFRUK.mjs.map → intent-resolver-KLBHSMW4.mjs.map} +0 -0
package/src/ScriptPlugin.tsx
CHANGED
|
@@ -5,12 +5,16 @@
|
|
|
5
5
|
// @ts-ignore
|
|
6
6
|
|
|
7
7
|
import { Capabilities, contributes, createIntent, defineModule, definePlugin, Events } from '@dxos/app-framework';
|
|
8
|
-
import {
|
|
9
|
-
import { FunctionType, ScriptType } from '@dxos/functions';
|
|
8
|
+
import { ScriptType } from '@dxos/functions';
|
|
10
9
|
import { RefArray } from '@dxos/live-object';
|
|
11
|
-
import {
|
|
10
|
+
import { ClientEvents } from '@dxos/plugin-client';
|
|
11
|
+
import { DeckCapabilities, DeckEvents } from '@dxos/plugin-deck';
|
|
12
|
+
import { SpaceCapabilities } from '@dxos/plugin-space';
|
|
13
|
+
import { defineObjectForm } from '@dxos/plugin-space/types';
|
|
14
|
+
import { getSpace } from '@dxos/react-client/echo';
|
|
12
15
|
|
|
13
|
-
import {
|
|
16
|
+
import { ArtifactDefinition, Compiler, IntentResolver, ReactSurface, ScriptSettings } from './capabilities';
|
|
17
|
+
import { ScriptEvents } from './events';
|
|
14
18
|
import { meta, SCRIPT_PLUGIN } from './meta';
|
|
15
19
|
import translations from './translations';
|
|
16
20
|
import { ScriptAction } from './types';
|
|
@@ -24,7 +28,7 @@ export const ScriptPlugin = () =>
|
|
|
24
28
|
}),
|
|
25
29
|
defineModule({
|
|
26
30
|
id: `${meta.id}/module/compiler`,
|
|
27
|
-
activatesOn:
|
|
31
|
+
activatesOn: ScriptEvents.SetupCompiler,
|
|
28
32
|
activate: Compiler,
|
|
29
33
|
}),
|
|
30
34
|
defineModule({
|
|
@@ -39,9 +43,6 @@ export const ScriptPlugin = () =>
|
|
|
39
43
|
contributes(Capabilities.Metadata, {
|
|
40
44
|
id: ScriptType.typename,
|
|
41
45
|
metadata: {
|
|
42
|
-
creationSchema: ScriptAction.CreateScriptSchema,
|
|
43
|
-
createObject: (props: ScriptAction.CreateScriptProps, { space }: { space: Space }) =>
|
|
44
|
-
createIntent(ScriptAction.Create, { ...props, space }),
|
|
45
46
|
placeholder: ['object title placeholder', { ns: SCRIPT_PLUGIN }],
|
|
46
47
|
icon: 'ph--code--regular',
|
|
47
48
|
// TODO(wittjosiah): Move out of metadata.
|
|
@@ -50,26 +51,45 @@ export const ScriptPlugin = () =>
|
|
|
50
51
|
}),
|
|
51
52
|
}),
|
|
52
53
|
defineModule({
|
|
53
|
-
id: `${meta.id}/module/
|
|
54
|
+
id: `${meta.id}/module/complementary-panels`,
|
|
55
|
+
activatesOn: DeckEvents.SetupComplementaryPanels,
|
|
56
|
+
activate: () =>
|
|
57
|
+
contributes(DeckCapabilities.ComplementaryPanel, {
|
|
58
|
+
id: 'function',
|
|
59
|
+
label: ['function panel label', { ns: SCRIPT_PLUGIN }],
|
|
60
|
+
icon: 'ph--terminal--regular',
|
|
61
|
+
fixed: true,
|
|
62
|
+
filter: (node) => node.data instanceof ScriptType && !!getSpace(node.data),
|
|
63
|
+
}),
|
|
64
|
+
}),
|
|
65
|
+
defineModule({
|
|
66
|
+
id: `${meta.id}/module/object-form`,
|
|
54
67
|
activatesOn: ClientEvents.SetupSchema,
|
|
55
|
-
activate: () =>
|
|
56
|
-
contributes(
|
|
57
|
-
|
|
58
|
-
|
|
68
|
+
activate: () =>
|
|
69
|
+
contributes(
|
|
70
|
+
SpaceCapabilities.ObjectForm,
|
|
71
|
+
defineObjectForm({
|
|
72
|
+
objectSchema: ScriptType,
|
|
73
|
+
formSchema: ScriptAction.CreateScriptSchema,
|
|
74
|
+
getIntent: (props, options) => createIntent(ScriptAction.Create, { ...props, space: options.space }),
|
|
75
|
+
}),
|
|
76
|
+
),
|
|
59
77
|
}),
|
|
60
78
|
defineModule({
|
|
61
79
|
id: `${meta.id}/module/react-surface`,
|
|
62
|
-
activatesOn: Events.
|
|
80
|
+
activatesOn: Events.SetupReactSurface,
|
|
81
|
+
// TODO(wittjosiah): Should occur before the script editor is loaded when surfaces activation is more granular.
|
|
82
|
+
activatesBefore: [ScriptEvents.SetupCompiler],
|
|
63
83
|
activate: ReactSurface,
|
|
64
84
|
}),
|
|
65
85
|
defineModule({
|
|
66
86
|
id: `${meta.id}/module/intent-resolver`,
|
|
67
|
-
activatesOn: Events.
|
|
87
|
+
activatesOn: Events.SetupIntentResolver,
|
|
68
88
|
activate: IntentResolver,
|
|
69
89
|
}),
|
|
70
90
|
defineModule({
|
|
71
|
-
id: `${meta.id}/module/artifact`,
|
|
72
|
-
activatesOn: Events.
|
|
73
|
-
activate:
|
|
91
|
+
id: `${meta.id}/module/artifact-definition`,
|
|
92
|
+
activatesOn: Events.SetupArtifactDefinition,
|
|
93
|
+
activate: ArtifactDefinition,
|
|
74
94
|
}),
|
|
75
95
|
]);
|
|
@@ -8,10 +8,11 @@ import { createArtifactElement } from '@dxos/assistant';
|
|
|
8
8
|
import { ObjectId, S } from '@dxos/echo-schema';
|
|
9
9
|
import { ScriptType } from '@dxos/functions';
|
|
10
10
|
import { invariant } from '@dxos/invariant';
|
|
11
|
+
import { SpaceAction } from '@dxos/plugin-space/types';
|
|
11
12
|
import { create, makeRef, type Space } from '@dxos/react-client/echo';
|
|
12
13
|
import { TextType } from '@dxos/schema';
|
|
13
|
-
import { SpaceAction } from '@dxos/plugin-space/types';
|
|
14
14
|
|
|
15
|
+
import { meta } from '../meta';
|
|
15
16
|
// TODO(burdon): Factor out.
|
|
16
17
|
declare global {
|
|
17
18
|
interface ToolContextExtensions {
|
|
@@ -22,10 +23,11 @@ declare global {
|
|
|
22
23
|
|
|
23
24
|
export default () => {
|
|
24
25
|
const definition = defineArtifact({
|
|
25
|
-
id:
|
|
26
|
+
id: meta.id,
|
|
27
|
+
name: meta.name,
|
|
26
28
|
// TODO(dmaretskyi): Since writing scripts requires a lot of domain knowledge,
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
+
// we should offload the work of synthesizing the code to a separate model.
|
|
30
|
+
// The main reasoning model will give it a concrete task and the script model will synthesize the code, knowing all the docs.
|
|
29
31
|
instructions: `
|
|
30
32
|
If the user explicitly requests you to write a script, you can create one.
|
|
31
33
|
If the user requests you to change one of the existing script, you can update it.
|
|
@@ -136,9 +138,10 @@ export default () => {
|
|
|
136
138
|
`,
|
|
137
139
|
schema: ScriptType,
|
|
138
140
|
tools: [
|
|
139
|
-
defineTool({
|
|
140
|
-
name: '
|
|
141
|
+
defineTool(meta.id, {
|
|
142
|
+
name: 'create',
|
|
141
143
|
description: 'Create a new script. Returns the artifact definition for the script',
|
|
144
|
+
caption: 'Creating script...',
|
|
142
145
|
schema: S.Struct({
|
|
143
146
|
name: S.String.annotations({ description: 'The name of the script' }),
|
|
144
147
|
code: S.String.annotations({
|
|
@@ -168,9 +171,10 @@ export default () => {
|
|
|
168
171
|
return ToolResult.Success(createArtifactElement(script.id));
|
|
169
172
|
},
|
|
170
173
|
}),
|
|
171
|
-
defineTool({
|
|
172
|
-
name: '
|
|
174
|
+
defineTool(meta.id, {
|
|
175
|
+
name: 'inspect',
|
|
173
176
|
description: 'Inspect a script. Returns the artifact definition for the script',
|
|
177
|
+
caption: 'Inspecting script...',
|
|
174
178
|
schema: S.Struct({
|
|
175
179
|
id: ObjectId,
|
|
176
180
|
}),
|
|
@@ -186,9 +190,10 @@ export default () => {
|
|
|
186
190
|
});
|
|
187
191
|
},
|
|
188
192
|
}),
|
|
189
|
-
defineTool({
|
|
190
|
-
name: '
|
|
193
|
+
defineTool(meta.id, {
|
|
194
|
+
name: 'update',
|
|
191
195
|
description: 'Update a script. Returns the artifact definition for the script',
|
|
196
|
+
caption: 'Updating script...',
|
|
192
197
|
schema: S.Struct({
|
|
193
198
|
id: ObjectId,
|
|
194
199
|
code: S.String.annotations({
|
|
@@ -8,6 +8,6 @@ export const Compiler = lazy(() => import('./compiler'));
|
|
|
8
8
|
export const IntentResolver = lazy(() => import('./intent-resolver'));
|
|
9
9
|
export const ReactSurface = lazy(() => import('./react-surface'));
|
|
10
10
|
export const ScriptSettings = lazy(() => import('./settings'));
|
|
11
|
-
export const
|
|
11
|
+
export const ArtifactDefinition = lazy(() => import('./artifact-definition'));
|
|
12
12
|
|
|
13
13
|
export * from './capabilities';
|
|
@@ -10,7 +10,8 @@ import { SettingsStore } from '@dxos/local-storage';
|
|
|
10
10
|
import { Clipboard } from '@dxos/react-ui';
|
|
11
11
|
|
|
12
12
|
import { ScriptCapabilities } from './capabilities';
|
|
13
|
-
import {
|
|
13
|
+
import { DebugPanel, ScriptSettings, ScriptContainer, ScriptSettingsPanel } from '../components';
|
|
14
|
+
import { useDeployState, useToolbarState } from '../hooks';
|
|
14
15
|
import { SCRIPT_PLUGIN } from '../meta';
|
|
15
16
|
import { type ScriptSettingsProps } from '../types';
|
|
16
17
|
|
|
@@ -36,10 +37,15 @@ export default () =>
|
|
|
36
37
|
}),
|
|
37
38
|
createSurface({
|
|
38
39
|
id: `${SCRIPT_PLUGIN}/automation`,
|
|
39
|
-
role: 'complementary--
|
|
40
|
+
role: 'complementary--function',
|
|
40
41
|
position: 'hoist',
|
|
41
42
|
filter: (data): data is { subject: ScriptType } => data.subject instanceof ScriptType,
|
|
42
|
-
component: ({ data }) =>
|
|
43
|
+
component: ({ data }) => {
|
|
44
|
+
// TODO(wittjosiah): Decouple hooks from toolbar state.
|
|
45
|
+
const state = useToolbarState({ view: 'editor' });
|
|
46
|
+
useDeployState({ state, script: data.subject });
|
|
47
|
+
return <DebugPanel functionUrl={state.functionUrl} />;
|
|
48
|
+
},
|
|
43
49
|
}),
|
|
44
50
|
createSurface({
|
|
45
51
|
id: `${SCRIPT_PLUGIN}/settings-panel`,
|
|
@@ -7,7 +7,7 @@ import React, { forwardRef, useEffect, useRef, useState } from 'react';
|
|
|
7
7
|
import { log } from '@dxos/log';
|
|
8
8
|
import { Avatar, Icon, Input, type ThemedClassName, Toolbar, useTranslation } from '@dxos/react-ui';
|
|
9
9
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
10
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
10
|
+
import { errorText, mx } from '@dxos/react-ui-theme';
|
|
11
11
|
|
|
12
12
|
import { SCRIPT_PLUGIN } from '../../meta';
|
|
13
13
|
|
|
@@ -232,7 +232,7 @@ const MessageItem = ({ classNames, message }: ThemedClassName<{ message: Message
|
|
|
232
232
|
const wrapper = 'p-1 px-2 rounded-lg bg-hoverSurface overflow-auto';
|
|
233
233
|
return (
|
|
234
234
|
<div className={mx('flex', type === 'request' ? 'ml-[1rem] justify-end' : 'mr-[1rem]', classNames)}>
|
|
235
|
-
{error && <div className={mx(wrapper, 'whitespace-pre
|
|
235
|
+
{error && <div className={mx(wrapper, 'whitespace-pre', errorText)}>{String(error)}</div>}
|
|
236
236
|
|
|
237
237
|
{text !== undefined && (
|
|
238
238
|
<div className={mx(wrapper, type === 'request' && 'bg-primary-400 dark:bg-primary-600')}>
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
import React, { useEffect, useRef } from 'react';
|
|
6
6
|
|
|
7
7
|
import { clientServiceBundle } from '@dxos/client-protocol';
|
|
8
|
-
import { type
|
|
8
|
+
import { type BundleResult } from '@dxos/functions/bundler';
|
|
9
|
+
import { invariant } from '@dxos/invariant';
|
|
9
10
|
import { log } from '@dxos/log';
|
|
10
11
|
import { useClient } from '@dxos/react-client';
|
|
11
12
|
import { baseSurface, mx } from '@dxos/react-ui-theme';
|
|
@@ -14,7 +15,7 @@ import { createIFramePort } from '@dxos/rpc-tunnel';
|
|
|
14
15
|
|
|
15
16
|
export type FrameContainerProps = {
|
|
16
17
|
containerUrl: string;
|
|
17
|
-
result:
|
|
18
|
+
result: BundleResult;
|
|
18
19
|
debug?: boolean;
|
|
19
20
|
};
|
|
20
21
|
|
|
@@ -45,6 +46,7 @@ export const FrameContainer = ({ containerUrl, result, debug = true }: FrameCont
|
|
|
45
46
|
}, [iframeRef]);
|
|
46
47
|
|
|
47
48
|
// Encodes compiled code via URL.
|
|
49
|
+
invariant(result.sourceHash, 'Source hash is required.');
|
|
48
50
|
const sourceHash = Buffer.from(result.sourceHash).toString('hex');
|
|
49
51
|
const src = result.bundle && `${containerUrl}?ts=${sourceHash}#code=${encodeURIComponent(result.bundle)}`;
|
|
50
52
|
|
|
@@ -57,7 +59,7 @@ export const FrameContainer = ({ containerUrl, result, debug = true }: FrameCont
|
|
|
57
59
|
<div
|
|
58
60
|
className={mx(
|
|
59
61
|
baseSurface,
|
|
60
|
-
'flex absolute right-2 bottom-2 w-[
|
|
62
|
+
'flex absolute right-2 bottom-2 w-[30rem] h-[200px] ring rounded',
|
|
61
63
|
'z-20 overflow-x-hidden overflow-y-auto',
|
|
62
64
|
)}
|
|
63
65
|
>
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
useTextEditor,
|
|
28
28
|
type UseTextEditorProps,
|
|
29
29
|
} from '@dxos/react-ui-editor';
|
|
30
|
-
import {
|
|
30
|
+
import { isNonNullable } from '@dxos/util';
|
|
31
31
|
|
|
32
32
|
export type TypescriptEditorProps = {
|
|
33
33
|
id: string;
|
|
@@ -94,7 +94,7 @@ export const TypescriptEditor = ({
|
|
|
94
94
|
]
|
|
95
95
|
: [],
|
|
96
96
|
],
|
|
97
|
-
].filter(
|
|
97
|
+
].filter(isNonNullable),
|
|
98
98
|
selection,
|
|
99
99
|
scrollTo,
|
|
100
100
|
}),
|
package/src/components/index.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import { lazy } from 'react';
|
|
6
6
|
|
|
7
|
+
export * from './DebugPanel';
|
|
7
8
|
export * from './ScriptSettings';
|
|
8
9
|
export * from './ScriptSettingsPanel';
|
|
9
10
|
|
|
10
|
-
export const AutomationPanel = lazy(() => import('./AutomationPanel'));
|
|
11
11
|
export const ScriptContainer = lazy(() => import('./ScriptContainer'));
|
package/src/events.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { defineEvent } from '@dxos/app-framework';
|
|
6
|
+
|
|
7
|
+
import { SCRIPT_PLUGIN } from './meta';
|
|
8
|
+
|
|
9
|
+
export namespace ScriptEvents {
|
|
10
|
+
export const SetupCompiler = defineEvent(`${SCRIPT_PLUGIN}/event/setup-compiler`);
|
|
11
|
+
}
|
package/src/hooks/deploy.ts
CHANGED
|
@@ -6,10 +6,10 @@ import { useCallback, useEffect, useMemo } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
FunctionType,
|
|
9
|
+
type ScriptType,
|
|
9
10
|
getInvocationUrl,
|
|
10
11
|
getUserFunctionUrlInMetadata,
|
|
11
12
|
incrementSemverPatch,
|
|
12
|
-
type ScriptType,
|
|
13
13
|
setUserFunctionUrlInMetadata,
|
|
14
14
|
uploadWorkerFunction,
|
|
15
15
|
} from '@dxos/functions';
|
|
@@ -105,7 +105,7 @@ export const useDeployHandler = ({ state, script }: { state: Partial<DeployState
|
|
|
105
105
|
const existingFunctionId = existingFunctionUrl?.split('/').at(-1);
|
|
106
106
|
|
|
107
107
|
const bundler = new Bundler({ platform: 'browser', sandboxedModules: [], remoteModules: {} });
|
|
108
|
-
const buildResult = await bundler.bundle(script.source.target!.content);
|
|
108
|
+
const buildResult = await bundler.bundle({ source: script.source.target!.content });
|
|
109
109
|
if (buildResult.error || !buildResult.bundle) {
|
|
110
110
|
throw buildResult.error;
|
|
111
111
|
}
|
package/src/hooks/view.ts
CHANGED
|
@@ -6,7 +6,8 @@ import { createMenuAction, createMenuItemGroup } from '@dxos/react-ui-menu';
|
|
|
6
6
|
|
|
7
7
|
import { SCRIPT_PLUGIN } from '../meta';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// TODO(wittjosiah): Replace debug with logs.
|
|
10
|
+
export type ViewType = 'editor' | 'split' | 'debug'; // 'logs';
|
|
10
11
|
|
|
11
12
|
export type ViewState = { view: ViewType };
|
|
12
13
|
|
|
@@ -16,6 +17,8 @@ const views: Record<ViewType, string> = {
|
|
|
16
17
|
editor: 'ph--code--regular',
|
|
17
18
|
split: 'ph--square-split-vertical--regular',
|
|
18
19
|
debug: 'ph--bug--regular',
|
|
20
|
+
// TODO(wittjosiah): Replace debug with logs.
|
|
21
|
+
// logs: 'ph--clock-counter-clockwise--regular',
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
const createViewGroupItem = (state: Partial<ViewState>) => {
|
package/src/templates/chat.ts
CHANGED
|
@@ -57,11 +57,11 @@ export default async ({
|
|
|
57
57
|
let prompt = promptObject.template;
|
|
58
58
|
for (const input of promptObject.inputs) {
|
|
59
59
|
let value: string | null = null;
|
|
60
|
-
if (input.type ===
|
|
60
|
+
if (input.type === TemplateInputType.VALUE) {
|
|
61
61
|
value = String(input.value);
|
|
62
|
-
} else if (input.type ===
|
|
62
|
+
} else if (input.type === TemplateInputType.RETRIEVER) {
|
|
63
63
|
value = await getContext({ space });
|
|
64
|
-
} else if (input.type ===
|
|
64
|
+
} else if (input.type === TemplateInputType.PASS_THROUGH) {
|
|
65
65
|
value = message.text;
|
|
66
66
|
}
|
|
67
67
|
if (value) {
|
|
@@ -90,7 +90,7 @@ const getContext = async ({ space }: any) => {
|
|
|
90
90
|
return context;
|
|
91
91
|
};
|
|
92
92
|
|
|
93
|
-
enum
|
|
93
|
+
enum TemplateInputType {
|
|
94
94
|
VALUE = 0,
|
|
95
95
|
PASS_THROUGH = 1,
|
|
96
96
|
RETRIEVER = 2,
|
package/src/templates/chess.ts
CHANGED
|
@@ -1,41 +1,48 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
|
|
1
|
+
// @ts-ignore
|
|
2
|
+
import { defineFunction, S } from 'dxos:functions';
|
|
5
3
|
// @ts-ignore
|
|
6
4
|
import { Chess } from 'https://esm.sh/chess.js@0.13.1';
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*/
|
|
11
|
-
export default async ({
|
|
12
|
-
event: {
|
|
13
|
-
data: { request },
|
|
14
|
-
},
|
|
15
|
-
context: { space },
|
|
16
|
-
}: any) => {
|
|
17
|
-
const {
|
|
18
|
-
data: {
|
|
19
|
-
objects: [gameId],
|
|
20
|
-
player = 'b',
|
|
21
|
-
},
|
|
22
|
-
} = await request.json();
|
|
6
|
+
export default defineFunction({
|
|
7
|
+
description: 'Plays a random move in a chess game.',
|
|
23
8
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
9
|
+
inputSchema: S.Struct({
|
|
10
|
+
changedObjectId: S.String.annotations({
|
|
11
|
+
description: 'The objects to play the game on.',
|
|
12
|
+
}),
|
|
13
|
+
player: S.optional(S.Literal('w', 'b')).annotations({
|
|
14
|
+
description: 'The player to play the game as.',
|
|
15
|
+
}),
|
|
16
|
+
}),
|
|
30
17
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
18
|
+
outputSchema: S.Struct({
|
|
19
|
+
state: S.String.annotations({
|
|
20
|
+
description: 'The state of the game as an ASCII art board.',
|
|
21
|
+
}),
|
|
22
|
+
}),
|
|
36
23
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
24
|
+
handler: async ({
|
|
25
|
+
event: {
|
|
26
|
+
data: { changedObjectId, player = 'b' },
|
|
27
|
+
},
|
|
28
|
+
context: { space },
|
|
29
|
+
}: any) => {
|
|
30
|
+
const { pgn } = await space.db.query({ id: changedObjectId }).first();
|
|
31
|
+
const game = new Chess();
|
|
32
|
+
game.load_pgn(pgn);
|
|
33
|
+
if (game.turn() !== player) {
|
|
34
|
+
return new Response('Invalid turn', { status: 409 });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const moves = game.moves();
|
|
38
|
+
const move = moves[Math.floor(Math.random() * moves.length)];
|
|
39
|
+
if (!move) {
|
|
40
|
+
return new Response('No legal moves', { status: 406 });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
game.move(move);
|
|
44
|
+
const newPgn = game.pgn();
|
|
45
|
+
await space.db.update({ id: changedObjectId }, { pgn: newPgn, fen: game.fen() });
|
|
46
|
+
return { state: game.ascii() };
|
|
47
|
+
},
|
|
48
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
import { createStatic, EchoObject, defineFunction, DXN, Filter, ObjectId, S } from 'dxos:functions';
|
|
7
|
+
import {
|
|
8
|
+
FetchHttpClient,
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
} from 'https://esm.sh/@effect/platform@0.77.2?deps=effect@3.13.3';
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
import { DiscordConfig, DiscordREST, DiscordRESTMemoryLive } from 'https://esm.sh/dfx@0.113.0?deps=effect@3.13.3';
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
import { Effect, Config, Redacted, Ref } from 'https://esm.sh/effect@3.13.3';
|
|
15
|
+
|
|
16
|
+
const MessageSchema = S.Struct({
|
|
17
|
+
id: S.String,
|
|
18
|
+
foreignId: S.Any, // bigint?
|
|
19
|
+
from: S.String,
|
|
20
|
+
created: S.String,
|
|
21
|
+
content: S.String,
|
|
22
|
+
}).pipe(EchoObject('example.com/type/Message', '0.1.0'));
|
|
23
|
+
|
|
24
|
+
const DEFAULT_AFTER = 1704067200; // 2024-01-01
|
|
25
|
+
|
|
26
|
+
const generateSnowflake = (unixTimestamp: number): bigint => {
|
|
27
|
+
const discordEpoch = 1420070400000n; // Discord Epoch (ms)
|
|
28
|
+
return (BigInt(unixTimestamp * 1000) - discordEpoch) << 22n;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default defineFunction({
|
|
32
|
+
inputSchema: S.Struct({
|
|
33
|
+
// TODO(wittjosiah): Remove. This is used to provide a terminal for a cron trigger.
|
|
34
|
+
tick: S.optional(S.String),
|
|
35
|
+
channelId: S.String,
|
|
36
|
+
after: S.optional(S.Number),
|
|
37
|
+
pageSize: S.optional(S.Number),
|
|
38
|
+
queueId: S.String,
|
|
39
|
+
}),
|
|
40
|
+
|
|
41
|
+
outputSchema: S.Struct({
|
|
42
|
+
newMessages: S.Number,
|
|
43
|
+
}),
|
|
44
|
+
|
|
45
|
+
handler: ({
|
|
46
|
+
event: {
|
|
47
|
+
data: { channelId, queueId, after = DEFAULT_AFTER, pageSize = 5 },
|
|
48
|
+
},
|
|
49
|
+
context: { space },
|
|
50
|
+
}: any) =>
|
|
51
|
+
Effect.gen(function* () {
|
|
52
|
+
const { token } = yield* Effect.tryPromise(() =>
|
|
53
|
+
space.db.query(Filter.typename('dxos.org/type/AccessToken', { source: 'discord.com' })).first(),
|
|
54
|
+
);
|
|
55
|
+
const { objects } = yield* Effect.tryPromise(() => space.queues.queryQueue(DXN.parse(queueId)));
|
|
56
|
+
const backfill = objects.length === 0;
|
|
57
|
+
const newMessages = yield* Ref.make(0);
|
|
58
|
+
const lastMessage = yield* Ref.make(objects.at(-1));
|
|
59
|
+
|
|
60
|
+
const enqueueMessages = Effect.gen(function* () {
|
|
61
|
+
const rest = yield* DiscordREST;
|
|
62
|
+
|
|
63
|
+
while (true) {
|
|
64
|
+
const last = yield* Ref.get(lastMessage);
|
|
65
|
+
const options = {
|
|
66
|
+
after: backfill && !last ? `${generateSnowflake(after)}` : last?.foreignId,
|
|
67
|
+
limit: pageSize,
|
|
68
|
+
};
|
|
69
|
+
const messages = yield* rest.getChannelMessages(channelId, options).pipe((res: any) => res.json);
|
|
70
|
+
const queueMessages = messages
|
|
71
|
+
.map((message: any) =>
|
|
72
|
+
createStatic(MessageSchema, {
|
|
73
|
+
id: ObjectId.random(),
|
|
74
|
+
foreignId: message.id,
|
|
75
|
+
from: message.author.username,
|
|
76
|
+
created: message.timestamp,
|
|
77
|
+
content: message.content,
|
|
78
|
+
}),
|
|
79
|
+
)
|
|
80
|
+
.toReversed();
|
|
81
|
+
if (queueMessages.length > 0) {
|
|
82
|
+
yield* Effect.tryPromise(() => space.queues.insertIntoQueue(DXN.parse(queueId), queueMessages));
|
|
83
|
+
yield* Ref.update(newMessages, (n: any) => n + queueMessages.length);
|
|
84
|
+
yield* Ref.update(lastMessage, (m: any) => queueMessages.at(-1));
|
|
85
|
+
}
|
|
86
|
+
if (messages.length < pageSize) {
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}).pipe(
|
|
91
|
+
Effect.provide(DiscordRESTMemoryLive),
|
|
92
|
+
Effect.provide(
|
|
93
|
+
DiscordConfig.layerConfig({
|
|
94
|
+
token: Config.succeed(Redacted.make(token)),
|
|
95
|
+
}),
|
|
96
|
+
),
|
|
97
|
+
Effect.provide(FetchHttpClient.layer),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
yield* enqueueMessages;
|
|
101
|
+
return { newMessages: yield* Ref.get(newMessages) };
|
|
102
|
+
}),
|
|
103
|
+
});
|
|
@@ -7,36 +7,35 @@ import { defineFunction, S } from 'dxos:functions';
|
|
|
7
7
|
import {
|
|
8
8
|
HttpClient,
|
|
9
9
|
HttpClientRequest,
|
|
10
|
-
|
|
11
|
-
HttpClientError,
|
|
10
|
+
FetchHttpClient,
|
|
12
11
|
// @ts-ignore
|
|
13
|
-
} from 'https://esm.sh/@effect/platform@0.
|
|
12
|
+
} from 'https://esm.sh/@effect/platform@0.77.2?deps=effect@3.13.3';
|
|
14
13
|
// @ts-ignore
|
|
15
|
-
import
|
|
16
|
-
// @ts-ignore
|
|
17
|
-
import { Effect, Schedule } from 'https://esm.sh/effect@3.6.0';
|
|
14
|
+
import { Effect, Schedule } from 'https://esm.sh/effect@3.13.3';
|
|
18
15
|
|
|
19
|
-
/**
|
|
20
|
-
* Returns the exchange rate between two currencies.
|
|
21
|
-
*/
|
|
22
16
|
export default defineFunction({
|
|
17
|
+
description: 'Returns the exchange rate between two currencies.',
|
|
18
|
+
|
|
23
19
|
inputSchema: S.Struct({
|
|
24
20
|
from: S.String.annotations({ description: 'The source currency' }),
|
|
25
21
|
to: S.String.annotations({ description: 'The target currency' }),
|
|
26
22
|
}),
|
|
27
23
|
|
|
28
|
-
outputSchema: S.String,
|
|
24
|
+
outputSchema: S.String.annotations({ description: 'The exchange rate between the two currencies' }),
|
|
29
25
|
|
|
30
26
|
handler: async ({
|
|
31
27
|
event: {
|
|
32
28
|
data: { from, to },
|
|
33
29
|
},
|
|
34
30
|
}: any) =>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
Effect.gen(function* () {
|
|
32
|
+
const res = yield* HttpClientRequest.get(`https://free.ratesdb.com/v1/rates?from=${from}&to=${to}`).pipe(
|
|
33
|
+
HttpClient.execute,
|
|
34
|
+
Effect.flatMap((res: any) => res.json),
|
|
35
|
+
Effect.timeout('1 second'),
|
|
36
|
+
Effect.retry(Schedule.exponential(1000).pipe(Schedule.compose(Schedule.recurs(3)))),
|
|
37
|
+
Effect.scoped,
|
|
38
|
+
);
|
|
39
|
+
return res.data.rates[to].toString();
|
|
40
|
+
}).pipe(Effect.provide(FetchHttpClient.layer)),
|
|
42
41
|
});
|
package/src/templates/forex.ts
CHANGED
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
// @ts-ignore
|
|
6
6
|
import { defineFunction, S } from 'dxos:functions';
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
* Returns the exchange rate between two currencies.
|
|
10
|
-
*/
|
|
11
8
|
export default defineFunction({
|
|
9
|
+
description: 'Returns the exchange rate between two currencies.',
|
|
10
|
+
|
|
12
11
|
inputSchema: S.Struct({
|
|
13
12
|
from: S.String.annotations({ description: 'The source currency' }),
|
|
14
13
|
to: S.String.annotations({ description: 'The target currency' }),
|
package/src/templates/index.ts
CHANGED
|
@@ -7,13 +7,15 @@ import chat from './chat.ts?raw';
|
|
|
7
7
|
// @ts-ignore
|
|
8
8
|
import chess from './chess.ts?raw';
|
|
9
9
|
// @ts-ignore
|
|
10
|
+
import discord from './discord.ts?raw';
|
|
11
|
+
// @ts-ignore
|
|
10
12
|
import echo from './echo.ts?raw';
|
|
11
13
|
// @ts-ignore
|
|
12
14
|
import email from './email.ts?raw';
|
|
13
15
|
// @ts-ignore
|
|
14
|
-
import forex from './forex.ts?raw';
|
|
15
|
-
// @ts-ignore
|
|
16
16
|
import forexEffect from './forex-effect.ts?raw';
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
import forex from './forex.ts?raw';
|
|
17
19
|
|
|
18
20
|
const removeHeader = (str: string) => str.split('\n').slice(4).join('\n');
|
|
19
21
|
|
|
@@ -58,4 +60,10 @@ export const templates: Template[] = [
|
|
|
58
60
|
source: removeHeader(email),
|
|
59
61
|
presetId: 'dxos.org/function/email',
|
|
60
62
|
},
|
|
63
|
+
{
|
|
64
|
+
id: 'dxos.org/script/discord',
|
|
65
|
+
name: 'Discord',
|
|
66
|
+
source: removeHeader(discord),
|
|
67
|
+
presetId: 'dxos.org/function/discord',
|
|
68
|
+
},
|
|
61
69
|
];
|
package/src/translations.ts
CHANGED
|
@@ -49,7 +49,10 @@ export default [
|
|
|
49
49
|
'view editor label': 'Editor',
|
|
50
50
|
'view split label': 'Split',
|
|
51
51
|
'view debug label': 'Debug',
|
|
52
|
+
// TODO(wittjosiah): Replace debug with logs.
|
|
53
|
+
// 'view logs label': 'Logs',
|
|
52
54
|
'template select group label': 'Select template',
|
|
55
|
+
'function panel label': 'Debug Function',
|
|
53
56
|
},
|
|
54
57
|
},
|
|
55
58
|
},
|