@jskit-ai/assistant 0.1.40 → 0.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -0
- package/package.descriptor.mjs +159 -102
- package/package.json +2 -2
- package/src/server/buildTemplateContext.js +7 -104
- package/src/server/pageSupport.js +117 -0
- package/src/server/subcommands/page.js +105 -0
- package/src/server/subcommands/settingsPage.js +109 -0
- package/src/server/subcommands/support.js +108 -0
- package/src/server/support.js +78 -0
- package/test/buildTemplateContext.test.js +20 -27
- package/test/packageDescriptor.test.js +20 -24
- package/test/subcommands.test.js +283 -0
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# @jskit-ai/assistant
|
|
2
|
+
|
|
3
|
+
Install assistant runtime/config for one surface, then scaffold assistant pages at explicit page files.
|
|
4
|
+
|
|
5
|
+
## Mental Model
|
|
6
|
+
|
|
7
|
+
`@jskit-ai/assistant` is the generator package.
|
|
8
|
+
|
|
9
|
+
- `setup` installs the assistant runtime/config/env wiring.
|
|
10
|
+
- `page <target-file>` creates the assistant runtime page anywhere relative to `src/pages/...`.
|
|
11
|
+
- `settings-page <target-file>` creates the assistant settings page anywhere relative to `src/pages/...`.
|
|
12
|
+
|
|
13
|
+
There is no separate server command and no separate client command.
|
|
14
|
+
|
|
15
|
+
The layering is:
|
|
16
|
+
|
|
17
|
+
- `@jskit-ai/assistant`: generator package with `setup`, `page`, and `settings-page`
|
|
18
|
+
- `@jskit-ai/assistant-runtime`: runtime package installed by `setup`
|
|
19
|
+
- `@jskit-ai/assistant-core`: lower-level provider/client library pulled in by `assistant-runtime`
|
|
20
|
+
|
|
21
|
+
So the normal flow is:
|
|
22
|
+
|
|
23
|
+
- run `setup`
|
|
24
|
+
- optionally create `page`
|
|
25
|
+
- optionally create `settings-page`
|
|
26
|
+
|
|
27
|
+
Page placement follows the same file-driven model as `@jskit-ai/ui-generator`.
|
|
28
|
+
|
|
29
|
+
- The surface comes from where the page file lives.
|
|
30
|
+
- The route URL comes from that file path.
|
|
31
|
+
- The default page-link placement comes from the nearest real parent subpages host when there is one.
|
|
32
|
+
|
|
33
|
+
Only one assistant can be installed per target surface. That rule is enforced by `setup`, which writes the per-surface config entries.
|
|
34
|
+
|
|
35
|
+
## Setup
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx jskit generate @jskit-ai/assistant setup \
|
|
39
|
+
--surface admin \
|
|
40
|
+
--settings-surface console \
|
|
41
|
+
--config-scope global \
|
|
42
|
+
--ai-provider openai \
|
|
43
|
+
--ai-api-key "$OPENAI_API_KEY" \
|
|
44
|
+
--ai-base-url "" \
|
|
45
|
+
--ai-timeout-ms 120000
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`setup` does not create pages.
|
|
49
|
+
|
|
50
|
+
It only registers:
|
|
51
|
+
|
|
52
|
+
- `config.assistantSurfaces.<surface>`
|
|
53
|
+
- `config.assistantServer.<surface>`
|
|
54
|
+
- prefixed AI env defaults in `.env`
|
|
55
|
+
- the `@jskit-ai/assistant-runtime` dependency chain, including `@jskit-ai/assistant-core`
|
|
56
|
+
|
|
57
|
+
## Runtime Page
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx jskit generate @jskit-ai/assistant page \
|
|
61
|
+
admin/ops/copilot/index.vue
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The page surface id comes from the target file path.
|
|
65
|
+
|
|
66
|
+
Optional page-link overrides:
|
|
67
|
+
|
|
68
|
+
- `--name`
|
|
69
|
+
- `--link-placement`
|
|
70
|
+
- `--link-component-token`
|
|
71
|
+
- `--link-to`
|
|
72
|
+
|
|
73
|
+
## Settings Page
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx jskit generate @jskit-ai/assistant settings-page \
|
|
77
|
+
admin/settings/index/assistant/index.vue \
|
|
78
|
+
--surface admin
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
For `settings-page`, the page location and the assistant target surface are separate concerns:
|
|
82
|
+
|
|
83
|
+
- `<target-file>` decides where the settings page lives and how its page link is placed
|
|
84
|
+
- `--surface` decides which assistant surface the settings page edits
|
|
85
|
+
|
|
86
|
+
The same optional page-link overrides are supported:
|
|
87
|
+
|
|
88
|
+
- `--name`
|
|
89
|
+
- `--link-placement`
|
|
90
|
+
- `--link-component-token`
|
|
91
|
+
- `--link-to`
|
package/package.descriptor.mjs
CHANGED
|
@@ -1,58 +1,70 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/assistant",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.41",
|
|
5
5
|
kind: "generator",
|
|
6
|
-
description: "
|
|
6
|
+
description: "Install assistant runtime/config for one surface and scaffold assistant pages at explicit target files.",
|
|
7
7
|
options: {
|
|
8
8
|
surface: {
|
|
9
9
|
required: true,
|
|
10
10
|
inputType: "text",
|
|
11
|
+
validationType: "enabled-surface-id",
|
|
11
12
|
defaultFromConfig: "surfaceDefaultId",
|
|
12
13
|
promptLabel: "Assistant surface",
|
|
13
|
-
promptHint: "
|
|
14
|
+
promptHint: "Assistant runtime surface id. Used by setup, or selected assistant surface for settings-page.",
|
|
15
|
+
helpHint: "For setup, this is the runtime surface receiving assistant wiring. For settings-page, this is the assistant runtime configured by that page."
|
|
14
16
|
},
|
|
15
17
|
"settings-surface": {
|
|
16
18
|
required: true,
|
|
17
19
|
inputType: "text",
|
|
20
|
+
validationType: "enabled-surface-id",
|
|
18
21
|
defaultValue: "",
|
|
19
22
|
promptLabel: "Settings surface",
|
|
20
|
-
promptHint: "Enabled surface id
|
|
21
|
-
|
|
22
|
-
"settings-route-path": {
|
|
23
|
-
required: false,
|
|
24
|
-
inputType: "text",
|
|
25
|
-
defaultValue: "assistant",
|
|
26
|
-
promptLabel: "Settings route path",
|
|
27
|
-
promptHint: "Route segment to use for the assistant settings section page."
|
|
23
|
+
promptHint: "Enabled settings host surface id used by assistant setup.",
|
|
24
|
+
helpHint: "Surface that hosts the settings UI for the selected assistant runtime."
|
|
28
25
|
},
|
|
29
26
|
"config-scope": {
|
|
30
27
|
required: true,
|
|
31
28
|
inputType: "text",
|
|
32
29
|
defaultValue: "global",
|
|
33
30
|
promptLabel: "Config scope",
|
|
34
|
-
promptHint: "global | workspace. Workspace scope requires both
|
|
31
|
+
promptHint: "global | workspace. Workspace scope requires both setup surfaces to requireWorkspace=true."
|
|
35
32
|
},
|
|
36
|
-
|
|
33
|
+
name: {
|
|
37
34
|
required: false,
|
|
38
35
|
inputType: "text",
|
|
39
36
|
defaultValue: "",
|
|
40
|
-
promptLabel: "
|
|
41
|
-
promptHint: "Optional
|
|
37
|
+
promptLabel: "Page label",
|
|
38
|
+
promptHint: "Optional page link label override for page and settings-page.",
|
|
39
|
+
helpLabel: "Display label"
|
|
40
|
+
},
|
|
41
|
+
force: {
|
|
42
|
+
required: false,
|
|
43
|
+
inputType: "flag",
|
|
44
|
+
defaultValue: "",
|
|
45
|
+
promptLabel: "Force overwrite",
|
|
46
|
+
promptHint: "Overwrite the generated page file if it already exists."
|
|
42
47
|
},
|
|
43
|
-
"placement
|
|
48
|
+
"link-placement": {
|
|
44
49
|
required: false,
|
|
45
50
|
inputType: "text",
|
|
46
|
-
defaultValue: "
|
|
47
|
-
promptLabel: "
|
|
48
|
-
promptHint: "
|
|
51
|
+
defaultValue: "",
|
|
52
|
+
promptLabel: "Link placement",
|
|
53
|
+
promptHint: "Optional host:position target for the generated page link placement."
|
|
49
54
|
},
|
|
50
|
-
"
|
|
55
|
+
"link-component-token": {
|
|
51
56
|
required: false,
|
|
52
57
|
inputType: "text",
|
|
53
|
-
defaultValue: "
|
|
54
|
-
promptLabel: "
|
|
55
|
-
promptHint: "
|
|
58
|
+
defaultValue: "",
|
|
59
|
+
promptLabel: "Link component token",
|
|
60
|
+
promptHint: "Optional component token override for the generated page link placement."
|
|
61
|
+
},
|
|
62
|
+
"link-to": {
|
|
63
|
+
required: false,
|
|
64
|
+
inputType: "text",
|
|
65
|
+
defaultValue: "",
|
|
66
|
+
promptLabel: "Link to",
|
|
67
|
+
promptHint: "Optional explicit props.to value for the generated page link placement."
|
|
56
68
|
},
|
|
57
69
|
"ai-config-prefix": {
|
|
58
70
|
required: false,
|
|
@@ -102,11 +114,131 @@ export default Object.freeze({
|
|
|
102
114
|
}
|
|
103
115
|
},
|
|
104
116
|
metadata: {
|
|
117
|
+
generatorPrimarySubcommand: "setup",
|
|
118
|
+
generatorSubcommands: {
|
|
119
|
+
setup: {
|
|
120
|
+
description: "Install assistant runtime/config for one target surface without creating pages.",
|
|
121
|
+
optionNames: [
|
|
122
|
+
"surface",
|
|
123
|
+
"settings-surface",
|
|
124
|
+
"config-scope",
|
|
125
|
+
"ai-config-prefix",
|
|
126
|
+
"ai-provider",
|
|
127
|
+
"ai-api-key",
|
|
128
|
+
"ai-base-url",
|
|
129
|
+
"ai-timeout-ms"
|
|
130
|
+
],
|
|
131
|
+
notes: [
|
|
132
|
+
"setup installs runtime/config only. It does not create assistant pages.",
|
|
133
|
+
"--surface selects the assistant runtime surface, and --settings-surface selects the surface that will host its settings UI."
|
|
134
|
+
],
|
|
135
|
+
examples: [
|
|
136
|
+
{
|
|
137
|
+
label: "Common usage",
|
|
138
|
+
lines: [
|
|
139
|
+
"npx jskit generate assistant setup \\",
|
|
140
|
+
" --surface admin \\",
|
|
141
|
+
" --settings-surface admin \\",
|
|
142
|
+
" --config-scope workspace \\",
|
|
143
|
+
" --ai-api-key \"$OPENAI_API_KEY\""
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
label: "More advanced usage",
|
|
148
|
+
lines: [
|
|
149
|
+
"npx jskit generate assistant setup \\",
|
|
150
|
+
" --surface console \\",
|
|
151
|
+
" --settings-surface console \\",
|
|
152
|
+
" --config-scope global \\",
|
|
153
|
+
" --ai-config-prefix CONSOLE_ASSISTANT \\",
|
|
154
|
+
" --ai-provider openai \\",
|
|
155
|
+
" --ai-api-key \"$OPENAI_API_KEY\" \\",
|
|
156
|
+
" --ai-base-url \"http://localhost:11434/v1\" \\",
|
|
157
|
+
" --ai-timeout-ms 60000"
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
},
|
|
162
|
+
page: {
|
|
163
|
+
entrypoint: "src/server/subcommands/page.js",
|
|
164
|
+
export: "runGeneratorSubcommand",
|
|
165
|
+
description: "Create an assistant runtime page at an explicit target file relative to src/pages/.",
|
|
166
|
+
positionalArgs: [
|
|
167
|
+
{
|
|
168
|
+
name: "target-file",
|
|
169
|
+
required: true,
|
|
170
|
+
descriptionKey: "page-target-file"
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
optionNames: ["name", "link-placement", "link-component-token", "link-to", "force"],
|
|
174
|
+
notes: [
|
|
175
|
+
"The target file decides where the page lives.",
|
|
176
|
+
"Page-link placement follows the same inference rules as ui-generator page.",
|
|
177
|
+
"If the target page file already exists, rerun with --force to overwrite it."
|
|
178
|
+
],
|
|
179
|
+
examples: [
|
|
180
|
+
{
|
|
181
|
+
label: "Common usage",
|
|
182
|
+
lines: [
|
|
183
|
+
"npx jskit generate assistant page \\",
|
|
184
|
+
" admin/assistant/index.vue"
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
label: "More advanced usage",
|
|
189
|
+
lines: [
|
|
190
|
+
"npx jskit generate assistant page \\",
|
|
191
|
+
" admin/ops/copilot/index.vue \\",
|
|
192
|
+
" --name \"Copilot\" \\",
|
|
193
|
+
" --link-placement shell-layout:top-right"
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
},
|
|
198
|
+
"settings-page": {
|
|
199
|
+
entrypoint: "src/server/subcommands/settingsPage.js",
|
|
200
|
+
export: "runGeneratorSubcommand",
|
|
201
|
+
description: "Create an assistant settings page at an explicit target file relative to src/pages/.",
|
|
202
|
+
positionalArgs: [
|
|
203
|
+
{
|
|
204
|
+
name: "target-file",
|
|
205
|
+
required: true,
|
|
206
|
+
descriptionKey: "page-target-file"
|
|
207
|
+
}
|
|
208
|
+
],
|
|
209
|
+
optionNames: ["surface", "name", "link-placement", "link-component-token", "link-to", "force"],
|
|
210
|
+
requiredOptionNames: ["surface"],
|
|
211
|
+
notes: [
|
|
212
|
+
"The target file decides where the settings page lives.",
|
|
213
|
+
"--surface selects the assistant runtime being configured. It does not place the file.",
|
|
214
|
+
"If the target page file already exists, rerun with --force to overwrite it."
|
|
215
|
+
],
|
|
216
|
+
examples: [
|
|
217
|
+
{
|
|
218
|
+
label: "Common usage",
|
|
219
|
+
lines: [
|
|
220
|
+
"npx jskit generate assistant settings-page \\",
|
|
221
|
+
" admin/settings/index/assistant/index.vue \\",
|
|
222
|
+
" --surface admin"
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
label: "More advanced usage",
|
|
227
|
+
lines: [
|
|
228
|
+
"npx jskit generate assistant settings-page \\",
|
|
229
|
+
" admin/settings/index/app-assistant/index.vue \\",
|
|
230
|
+
" --surface app \\",
|
|
231
|
+
" --name \"App Assistant\""
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
},
|
|
105
237
|
apiSummary: {
|
|
106
238
|
surfaces: [
|
|
107
239
|
{
|
|
108
240
|
subpath: "./server/buildTemplateContext",
|
|
109
|
-
summary: "Builds deterministic assistant
|
|
241
|
+
summary: "Builds deterministic assistant setup template context values from app surface metadata."
|
|
110
242
|
}
|
|
111
243
|
],
|
|
112
244
|
containerTokens: {
|
|
@@ -124,83 +256,8 @@ export default Object.freeze({
|
|
|
124
256
|
scripts: {}
|
|
125
257
|
},
|
|
126
258
|
procfile: {},
|
|
127
|
-
files: [
|
|
128
|
-
{
|
|
129
|
-
from: "templates/src/pages/assistant/index.vue",
|
|
130
|
-
toSurface: "${option:surface|lower}",
|
|
131
|
-
toSurfacePath: "assistant/index.vue",
|
|
132
|
-
reason: "Install generated assistant runtime page.",
|
|
133
|
-
category: "assistant",
|
|
134
|
-
id: "assistant-page-runtime",
|
|
135
|
-
templateContext: {
|
|
136
|
-
entrypoint: "src/server/buildTemplateContext.js",
|
|
137
|
-
export: "buildTemplateContext"
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
from: "templates/src/pages/settings/assistant/index.vue",
|
|
142
|
-
toSurface: "${option:settings-surface|lower}",
|
|
143
|
-
toSurfacePath: "settings/${option:settings-route-path|path}/index.vue",
|
|
144
|
-
reason: "Install generated assistant settings section page.",
|
|
145
|
-
category: "assistant",
|
|
146
|
-
id: "assistant-page-settings-standard",
|
|
147
|
-
when: {
|
|
148
|
-
option: "settings-surface",
|
|
149
|
-
notEquals: "admin"
|
|
150
|
-
},
|
|
151
|
-
templateContext: {
|
|
152
|
-
entrypoint: "src/server/buildTemplateContext.js",
|
|
153
|
-
export: "buildTemplateContext"
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
from: "templates/src/pages/settings/assistant/index.vue",
|
|
158
|
-
toSurface: "${option:settings-surface|lower}",
|
|
159
|
-
toSurfacePath: "workspace/settings/${option:settings-route-path|path}/index.vue",
|
|
160
|
-
reason: "Install generated assistant settings section page.",
|
|
161
|
-
category: "assistant",
|
|
162
|
-
id: "assistant-page-settings-admin",
|
|
163
|
-
when: {
|
|
164
|
-
option: "settings-surface",
|
|
165
|
-
equals: "admin"
|
|
166
|
-
},
|
|
167
|
-
templateContext: {
|
|
168
|
-
entrypoint: "src/server/buildTemplateContext.js",
|
|
169
|
-
export: "buildTemplateContext"
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
],
|
|
259
|
+
files: [],
|
|
173
260
|
text: [
|
|
174
|
-
{
|
|
175
|
-
op: "append-text",
|
|
176
|
-
file: "src/placement.js",
|
|
177
|
-
position: "bottom",
|
|
178
|
-
skipIfContains: "assistant.generated.menu:${option:surface|lower}",
|
|
179
|
-
value:
|
|
180
|
-
"\n// assistant.generated.menu:${option:surface|lower}\naddPlacement({\n id: \"assistant.generated.menu.${option:surface|kebab}\",\n host: \"__ASSISTANT_MENU_PLACEMENT_HOST__\",\n position: \"__ASSISTANT_MENU_PLACEMENT_POSITION__\",\n surfaces: [\"${option:surface|lower}\"],\n order: 310,\n componentToken: \"__ASSISTANT_MENU_COMPONENT_TOKEN__\",\n props: {\n label: \"__ASSISTANT_MENU_LABEL__\",\n surface: \"${option:surface|lower}\",\n workspaceSuffix: \"__ASSISTANT_MENU_WORKSPACE_SUFFIX__\",\n nonWorkspaceSuffix: \"__ASSISTANT_MENU_NON_WORKSPACE_SUFFIX__\"\n },\n when: ({ auth }) => Boolean(auth?.authenticated)\n});\n",
|
|
181
|
-
reason: "Append generated assistant runtime menu placement into app-owned placement registry.",
|
|
182
|
-
category: "assistant",
|
|
183
|
-
id: "assistant-placement-menu",
|
|
184
|
-
templateContext: {
|
|
185
|
-
entrypoint: "src/server/buildTemplateContext.js",
|
|
186
|
-
export: "buildTemplateContext"
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
op: "append-text",
|
|
191
|
-
file: "src/placement.js",
|
|
192
|
-
position: "bottom",
|
|
193
|
-
skipIfContains: "assistant.generated.settings.menu:${option:surface|lower}",
|
|
194
|
-
value:
|
|
195
|
-
"\n// assistant.generated.settings.menu:${option:surface|lower}\naddPlacement({\n id: \"assistant.generated.settings.menu.${option:surface|kebab}\",\n host: \"__ASSISTANT_SETTINGS_HOST__\",\n position: \"primary-menu\",\n surfaces: [\"${option:settings-surface|lower}\"],\n order: 250,\n componentToken: \"users.web.shell.surface-aware-menu-link-item\",\n props: {\n label: \"__ASSISTANT_SETTINGS_MENU_LABEL__\",\n surface: \"${option:settings-surface|lower}\",\n workspaceSuffix: \"__ASSISTANT_SETTINGS_MENU_WORKSPACE_SUFFIX__\",\n nonWorkspaceSuffix: \"__ASSISTANT_SETTINGS_MENU_NON_WORKSPACE_SUFFIX__\"\n },\n when: ({ auth }) => Boolean(auth?.authenticated)\n});\n",
|
|
196
|
-
reason: "Append generated assistant settings section menu placement into app-owned settings placements.",
|
|
197
|
-
category: "assistant",
|
|
198
|
-
id: "assistant-settings-menu-placement",
|
|
199
|
-
templateContext: {
|
|
200
|
-
entrypoint: "src/server/buildTemplateContext.js",
|
|
201
|
-
export: "buildTemplateContext"
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
261
|
{
|
|
205
262
|
op: "append-text",
|
|
206
263
|
file: "config/public.js",
|
|
@@ -208,7 +265,7 @@ export default Object.freeze({
|
|
|
208
265
|
skipIfContains: "config.assistantSurfaces.${option:surface|lower} = {",
|
|
209
266
|
value:
|
|
210
267
|
"\nconfig.assistantSurfaces.${option:surface|lower} = {\n settingsSurfaceId: \"__ASSISTANT_SETTINGS_SURFACE_ID__\",\n configScope: \"__ASSISTANT_CONFIG_SCOPE__\"\n};\n",
|
|
211
|
-
reason: "Register the
|
|
268
|
+
reason: "Register the assistant runtime surface in public app config.",
|
|
212
269
|
category: "assistant",
|
|
213
270
|
id: "assistant-public-surface-config",
|
|
214
271
|
templateContext: {
|
|
@@ -223,7 +280,7 @@ export default Object.freeze({
|
|
|
223
280
|
skipIfContains: "config.assistantServer.${option:surface|lower} = {",
|
|
224
281
|
value:
|
|
225
282
|
"\nconfig.assistantServer.${option:surface|lower} = {\n aiConfigPrefix: \"__ASSISTANT_AI_CONFIG_PREFIX__\"\n};\n",
|
|
226
|
-
reason: "Register
|
|
283
|
+
reason: "Register assistant server config for the selected runtime surface.",
|
|
227
284
|
category: "assistant",
|
|
228
285
|
id: "assistant-server-surface-config",
|
|
229
286
|
templateContext: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/assistant",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.41",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -9,6 +9,6 @@
|
|
|
9
9
|
"./server/buildTemplateContext": "./src/server/buildTemplateContext.js"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@jskit-ai/kernel": "0.1.
|
|
12
|
+
"@jskit-ai/kernel": "0.1.32"
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -1,97 +1,16 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { pathToFileURL } from "node:url";
|
|
3
1
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const DEFAULT_MENU_COMPONENT_TOKEN = "users.web.shell.surface-aware-menu-link-item";
|
|
12
|
-
|
|
13
|
-
function normalizeConfigScope(value = "") {
|
|
14
|
-
const normalized = normalizeText(value).toLowerCase();
|
|
15
|
-
if (normalized === "global" || normalized === "workspace") {
|
|
16
|
-
return normalized;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
throw new Error('assistant generator option "config-scope" must be "global" or "workspace".');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async function loadAppConfig(appRoot = "") {
|
|
23
|
-
const publicConfigUrl = pathToFileURL(path.join(appRoot, "config", "public.js")).href;
|
|
24
|
-
return loadAppConfigFromModuleUrl({
|
|
25
|
-
moduleUrl: publicConfigUrl
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function resolveSurfaceDefinition(appConfig = {}, surfaceId = "", optionName = "surface") {
|
|
30
|
-
const normalizedSurfaceId = normalizeSurfaceId(surfaceId);
|
|
31
|
-
if (!normalizedSurfaceId) {
|
|
32
|
-
throw new Error(`assistant generator requires --${optionName}.`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const sourceDefinitions = appConfig && typeof appConfig.surfaceDefinitions === "object" && !Array.isArray(appConfig.surfaceDefinitions)
|
|
36
|
-
? appConfig.surfaceDefinitions
|
|
37
|
-
: {};
|
|
38
|
-
const rawDefinition = sourceDefinitions[normalizedSurfaceId];
|
|
39
|
-
if (!rawDefinition || typeof rawDefinition !== "object" || Array.isArray(rawDefinition)) {
|
|
40
|
-
throw new Error(`assistant generator surface "${normalizedSurfaceId}" is not defined in config/public.js.`);
|
|
41
|
-
}
|
|
42
|
-
if (rawDefinition.enabled === false) {
|
|
43
|
-
throw new Error(`assistant generator surface "${normalizedSurfaceId}" is disabled in config/public.js.`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return Object.freeze({
|
|
47
|
-
id: normalizedSurfaceId,
|
|
48
|
-
requiresWorkspace: rawDefinition.requiresWorkspace === true,
|
|
49
|
-
accessPolicyId: normalizeText(rawDefinition.accessPolicyId).toLowerCase()
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function assertAssistantSurfaceIsAvailable(appConfig = {}, surfaceId = "") {
|
|
54
|
-
const assistantSurfaces =
|
|
55
|
-
appConfig && typeof appConfig.assistantSurfaces === "object" && !Array.isArray(appConfig.assistantSurfaces)
|
|
56
|
-
? appConfig.assistantSurfaces
|
|
57
|
-
: {};
|
|
58
|
-
if (assistantSurfaces[surfaceId]) {
|
|
59
|
-
throw new Error(`assistant generator surface "${surfaceId}" already has an assistant configured in config/public.js.`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function resolveAiConfigPrefix(surfaceId = "", explicitPrefix = "") {
|
|
64
|
-
const normalizedExplicitPrefix = normalizeText(explicitPrefix);
|
|
65
|
-
if (normalizedExplicitPrefix) {
|
|
66
|
-
return normalizedExplicitPrefix;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const surfacePrefix = toSnakeCase(surfaceId).toUpperCase();
|
|
70
|
-
return surfacePrefix ? `${surfacePrefix}_ASSISTANT` : "ASSISTANT";
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function normalizeSettingsRoutePath(value = "") {
|
|
74
|
-
return normalizeText(value).toLowerCase().replace(/^\/+|\/+$/g, "") || "assistant";
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function resolveSettingsRouteBase(surfaceId = "") {
|
|
78
|
-
return normalizeSurfaceId(surfaceId) === "admin" ? "/workspace/settings" : "/settings";
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function resolveMenuPlacementTarget(appRoot = "", options = {}) {
|
|
82
|
-
return resolveShellOutletPlacementTargetFromApp({
|
|
83
|
-
appRoot,
|
|
84
|
-
context: "assistant-generator",
|
|
85
|
-
placement: normalizeText(options?.placement)
|
|
86
|
-
});
|
|
87
|
-
}
|
|
2
|
+
assertAssistantSurfaceIsAvailable,
|
|
3
|
+
loadAppConfig,
|
|
4
|
+
normalizeConfigScope,
|
|
5
|
+
resolveAiConfigPrefix,
|
|
6
|
+
resolveSurfaceDefinition
|
|
7
|
+
} from "./support.js";
|
|
88
8
|
|
|
89
9
|
async function buildTemplateContext({ appRoot, options } = {}) {
|
|
90
10
|
const appConfig = await loadAppConfig(appRoot);
|
|
91
11
|
const runtimeSurface = resolveSurfaceDefinition(appConfig, options?.surface, "surface");
|
|
92
12
|
const settingsSurface = resolveSurfaceDefinition(appConfig, options?.["settings-surface"], "settings-surface");
|
|
93
13
|
const configScope = normalizeConfigScope(options?.["config-scope"]);
|
|
94
|
-
const settingsRoutePath = normalizeSettingsRoutePath(options?.["settings-route-path"]);
|
|
95
14
|
|
|
96
15
|
assertAssistantSurfaceIsAvailable(appConfig, runtimeSurface.id);
|
|
97
16
|
|
|
@@ -106,26 +25,10 @@ async function buildTemplateContext({ appRoot, options } = {}) {
|
|
|
106
25
|
);
|
|
107
26
|
}
|
|
108
27
|
|
|
109
|
-
const placementTarget = await resolveMenuPlacementTarget(appRoot, options);
|
|
110
|
-
const menuComponentToken = normalizeText(options?.["placement-component-token"]) || DEFAULT_MENU_COMPONENT_TOKEN;
|
|
111
|
-
const settingsRouteBase = resolveSettingsRouteBase(settingsSurface.id);
|
|
112
|
-
const settingsMenuSuffix = `${settingsRouteBase}/${settingsRoutePath}`;
|
|
113
|
-
|
|
114
28
|
return Object.freeze({
|
|
115
|
-
"__ASSISTANT_SURFACE_ID__": runtimeSurface.id,
|
|
116
29
|
"__ASSISTANT_SETTINGS_SURFACE_ID__": settingsSurface.id,
|
|
117
30
|
"__ASSISTANT_CONFIG_SCOPE__": configScope,
|
|
118
|
-
"
|
|
119
|
-
"__ASSISTANT_AI_CONFIG_PREFIX__": resolveAiConfigPrefix(runtimeSurface.id, options?.["ai-config-prefix"]),
|
|
120
|
-
"__ASSISTANT_MENU_PLACEMENT_HOST__": placementTarget.host,
|
|
121
|
-
"__ASSISTANT_MENU_PLACEMENT_POSITION__": placementTarget.position,
|
|
122
|
-
"__ASSISTANT_MENU_COMPONENT_TOKEN__": menuComponentToken,
|
|
123
|
-
"__ASSISTANT_MENU_LABEL__": normalizeText(options?.["menu-label"]) || "Assistant",
|
|
124
|
-
"__ASSISTANT_MENU_WORKSPACE_SUFFIX__": "/assistant",
|
|
125
|
-
"__ASSISTANT_MENU_NON_WORKSPACE_SUFFIX__": "/assistant",
|
|
126
|
-
"__ASSISTANT_SETTINGS_MENU_LABEL__": normalizeText(options?.["menu-label"]) || "Assistant",
|
|
127
|
-
"__ASSISTANT_SETTINGS_MENU_WORKSPACE_SUFFIX__": settingsMenuSuffix,
|
|
128
|
-
"__ASSISTANT_SETTINGS_MENU_NON_WORKSPACE_SUFFIX__": settingsMenuSuffix
|
|
31
|
+
"__ASSISTANT_AI_CONFIG_PREFIX__": resolveAiConfigPrefix(runtimeSurface.id, options?.["ai-config-prefix"])
|
|
129
32
|
});
|
|
130
33
|
}
|
|
131
34
|
|