@schlenkr/ragents 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -4
- package/apps/server/src/plugin-support/language-server/contract.ts +6 -1
- package/apps/server/src/plugin-support/language-server/tools.ts +29 -4
- package/apps/server/src/plugin-support/run-scripts.ts +4 -1
- package/apps/server/src/plugin-support/start-entries-contract.ts +4 -3
- package/apps/server/src/plugin-support/workspace-sandbox-host.ts +2 -0
- package/apps/server/src/profile-target.ts +25 -0
- package/apps/server/src/web-build.ts +82 -0
- package/apps/server/tsconfig.json +38 -0
- package/apps/web/column.html +12 -0
- package/apps/web/index.html +12 -0
- package/apps/web/src/App.tsx +0 -10
- package/apps/web/src/PluginChat.tsx +8 -8
- package/apps/web/src/PluginRegistry.tsx +0 -12
- package/apps/web/src/StartSurface.tsx +2 -2
- package/apps/web/src/ThemeSettings.tsx +1 -1
- package/apps/web/src/column/ColumnApp.tsx +83 -7
- package/apps/web/src/column/clipboard.ts +64 -0
- package/apps/web/src/column/host-contract.ts +10 -0
- package/apps/web/src/column.tsx +2 -0
- package/apps/web/src/panel/LoginForm.tsx +9 -3
- package/apps/web/src/panel/OverviewPage.tsx +187 -62
- package/apps/web/src/panel/PanelDialogs.tsx +115 -0
- package/apps/web/src/panel/PanelPage.tsx +1 -1
- package/apps/web/src/panel/SettingsPage.tsx +75 -79
- package/apps/web/src/panel/contract.ts +17 -9
- package/apps/web/src/panel/target-state.ts +27 -2
- package/apps/web/src/ui/theme.css +4 -0
- package/apps/web/tsconfig.json +41 -0
- package/apps/web/vite.config.ts +140 -0
- package/package.json +6 -2
- package/packages/ragents/src/plugin-host.ts +5 -2
- package/packages/ragents/src/plugin-types.ts +3 -2
- package/packages/workspace-executor/src/context.ts +4 -0
- package/packages/workspace-executor/src/executor.ts +9 -2
- package/packages/workspace-executor/src/index.ts +2 -0
- package/packages/workspace-executor/src/language-server/adapters/fsharp.ts +1 -0
- package/packages/workspace-executor/src/language-server/adapters/roslyn.ts +1 -0
- package/packages/workspace-executor/src/language-server/adapters/typescript.ts +34 -6
- package/packages/workspace-executor/src/language-server/contract.ts +7 -3
- package/packages/workspace-executor/src/language-server/host.ts +275 -138
- package/plugins/ragents.actor-programs/contract.ts +1 -1
- package/plugins/ragents.actor-programs/prompt.hbs +4 -4
- package/plugins/ragents.actor-programs/server/app-project.ts +0 -2
- package/plugins/ragents.actor-programs/server/runtime.ts +1 -1
- package/plugins/ragents.actor-programs/web/AppsPanel.tsx +3 -5
- package/plugins/ragents.actor-programs/web/EmbeddedApps.tsx +7 -39
- package/plugins/ragents.actor-programs/web/api.ts +1 -10
- package/plugins/ragents.lsp-roslyn/web/language-server-plugin.tsx +73 -44
- package/plugins/ragents.orchestration/canvas.hbs +28 -44
- package/plugins/ragents.orchestration/contract.ts +17 -287
- package/plugins/ragents.orchestration/server/canvas-tool.ts +18 -123
- package/plugins/ragents.orchestration/tiled-layout.ts +12 -4
- package/plugins/ragents.orchestration/web/ActorCanvasControls.tsx +23 -47
- package/plugins/ragents.orchestration/web/ActorChatPreview.tsx +3 -9
- package/plugins/ragents.orchestration/web/ActorShortcut.tsx +3 -5
- package/plugins/ragents.orchestration/web/ActorTile.tsx +3 -2
- package/plugins/ragents.orchestration/web/CanvasTiles.tsx +3 -3
- package/plugins/ragents.orchestration/web/Center.tsx +28 -463
- package/plugins/ragents.orchestration/web/MaterialBody.tsx +6 -87
- package/plugins/ragents.orchestration/web/TiledCanvas.tsx +2 -3
- package/plugins/ragents.orchestration/web/canvas-view-settings.ts +7 -13
- package/plugins/ragents.orchestration/web/column/RunColumn.tsx +44 -19
- package/plugins/ragents.orchestration/web/column/column-state.ts +13 -4
- package/plugins/ragents.orchestration/web/constants.ts +0 -2
- package/plugins/ragents.orchestration/web/index.tsx +0 -6
- package/plugins/ragents.orchestration/web/tile-docking.ts +7 -1
- package/plugins/ragents.orchestration/web/tiled-view-settings.ts +7 -9
- package/plugins/ragents.overseer/server/coordinator-prompt.ts +1 -1
- package/plugins/ragents.profile-distribution/server/web.ts +3 -27
- package/plugins/ragents.reference/examples.ts +1 -2
- package/plugins/ragents.reference/run-scripts/balcony-wizard/RUN.md +1 -1
- package/plugins/ragents.reference/run-scripts/balcony-wizard/actors/balcony-app/package.json +1 -1
- package/plugins/ragents.reference/run-scripts/balcony-wizard/src/server.ts +1 -1
- package/plugins/ragents.reference/run-scripts/balcony-wizard/tests/program.test.ts +2 -2
- package/plugins/ragents.reference/run-scripts/conversation-circle/RUN.md +3 -4
- package/plugins/ragents.reference/run-scripts/conversation-circle/src/server.ts +9 -15
- package/plugins/ragents.reference/run-scripts/conversation-circle/tests/program.test.ts +5 -3
- package/plugins/ragents.reference/run-scripts/learning-afternoon/package.json +1 -1
- package/plugins/ragents.reference/run-scripts/learning-afternoon/src/server.ts +14 -4
- package/plugins/ragents.reference/run-scripts/word-game/src/server.ts +1 -1
- package/plugins/ragents.reference/skills/210-moderator-handover/SKILL.md +1 -1
- package/plugins/ragents.reference/walkthroughs.ts +14 -14
- package/plugins/ragents.workspace/client/workspace-client.ts +4 -1
- package/scripts/agent/agent-cli.ts +9 -24
- package/scripts/package/build-package.ts +18 -5
- package/scripts/package/publish-package.ts +24 -10
- package/scripts/package/ragents.mjs +3 -2
- package/scripts/remote/connect.ts +16 -5
- package/scripts/remote/start.ts +33 -15
- package/scripts/workspace-client/run-workspace-client.ts +3 -1
- package/apps/web/src/MaterialSettings.tsx +0 -40
- package/apps/web/src/material-settings.ts +0 -104
- package/plugins/ragents.orchestration/web/ActorNet.tsx +0 -607
- package/plugins/ragents.orchestration/web/CardSizeSettings.tsx +0 -42
- package/plugins/ragents.orchestration/web/ScriptActorCard.tsx +0 -56
- package/plugins/ragents.orchestration/web/ZoomSettings.tsx +0 -38
- package/plugins/ragents.orchestration/web/canvas-layout-storage.ts +0 -39
- package/plugins/ragents.orchestration/web/canvas-layout.ts +0 -326
- package/plugins/ragents.orchestration/web/canvas-scene.ts +0 -311
- package/plugins/ragents.orchestration/web/card-size-settings.ts +0 -36
- package/plugins/ragents.orchestration/web/keyboard-navigation.ts +0 -55
- package/plugins/ragents.orchestration/web/material-layout.ts +0 -14
- package/plugins/ragents.orchestration/web/useCanvasMeasurements.ts +0 -78
- package/plugins/ragents.orchestration/web/zoom-settings.ts +0 -28
- package/plugins/ragents.reference/skills/120-canvas-conversation-circle/SKILL.md +0 -12
- package/plugins/ragents.reference/skills/130-canvas-pipeline-with-approval/SKILL.md +0 -12
- package/plugins/ragents.reference/skills/140-canvas-team-tree-and-topic-grid/SKILL.md +0 -12
package/README.md
CHANGED
|
@@ -22,11 +22,15 @@ npm install -g @schlenkr/ragents
|
|
|
22
22
|
RAGENTS_TOKEN=<token> ragents connect https://ragents.example.com
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
- `start <profile>` - start a profile shipped with the package (`core`, `developer`)
|
|
26
|
-
`
|
|
25
|
+
- `start <profile|path>` - start a profile shipped with the package (`core`, `developer`), your
|
|
26
|
+
own `ragents.config.<name>.ts` anywhere on disk, or a stand that `connect` already fetched,
|
|
27
|
+
without asking the server. The web interface is built for that set of plugins when it is
|
|
28
|
+
missing or was last built for another profile or host version; the result goes to
|
|
29
|
+
`<data dir>/web/<profile>`, never into the package folder.
|
|
27
30
|
|
|
28
31
|
```sh
|
|
29
32
|
ragents start developer
|
|
33
|
+
ragents start ~/projects/mine/ragents.config.mine.ts
|
|
30
34
|
```
|
|
31
35
|
|
|
32
36
|
- `provision [<profile>|--workspace]` - fetch the tools the plugins of that profile need, such as
|
|
@@ -44,7 +48,8 @@ npm install -g @schlenkr/ragents
|
|
|
44
48
|
```
|
|
45
49
|
|
|
46
50
|
- `run <folder> "<task>"` - start the host if none answers, bind a new run to the folder and wait
|
|
47
|
-
until the turn is finished; the last line is always `run: <id>`.
|
|
51
|
+
until the turn is finished; the last line is always `run: <id>`. This host serves no web
|
|
52
|
+
interface - an agent does not need one; use `start` for that.
|
|
48
53
|
|
|
49
54
|
```sh
|
|
50
55
|
ragents run ~/projects/demo "Fix the type error in src/broken.ts"
|
|
@@ -71,7 +76,8 @@ npm install -g @schlenkr/ragents
|
|
|
71
76
|
|
|
72
77
|
## Requirements
|
|
73
78
|
|
|
74
|
-
- Node 22 (>= 22.19.0). No git, no pnpm, no
|
|
79
|
+
- Node 22 (>= 22.19.0). No git, no pnpm, no source checkout; the package builds its own web
|
|
80
|
+
interface per profile with the Vite it brings along.
|
|
75
81
|
- `dotnet` (.NET 10 SDK) for the C# diagnostics through Roslyn and the F# diagnostics through FSAC;
|
|
76
82
|
without it TypeScript diagnostics still work.
|
|
77
83
|
- Model access: either `OPENROUTER_API_KEY` in the environment, or `ragents connect <server-url>`
|
|
@@ -3,7 +3,12 @@ import { defineOperation } from "@aicontainer/ragents/src/rpc/contract";
|
|
|
3
3
|
import { openJson } from "@aicontainer/ragents/src/http/contracts";
|
|
4
4
|
import type { LanguageServerSnapshot } from "@aicontainer/workspace-executor";
|
|
5
5
|
|
|
6
|
-
export type {
|
|
6
|
+
export type {
|
|
7
|
+
LanguageServerInstanceSnapshot,
|
|
8
|
+
LanguageServerSeverity,
|
|
9
|
+
LanguageServerSnapshot,
|
|
10
|
+
LanguageServerState,
|
|
11
|
+
} from "@aicontainer/workspace-executor";
|
|
7
12
|
|
|
8
13
|
/** Je Sprachserver-Plugin eine Methode; die Rechte tragen seine Kennung. */
|
|
9
14
|
export const languageServerSnapshotContract = (pluginId: string) =>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { defineRunFunction, type ToolContributor } from "@aicontainer/ragents";
|
|
3
3
|
import {
|
|
4
|
+
languageServerCloseTool,
|
|
4
5
|
languageServerDiagnosticsTool,
|
|
5
6
|
languageServerOpenTool,
|
|
6
7
|
type LanguageServerAdapter,
|
|
@@ -19,15 +20,22 @@ export const createLanguageServerToolContributor = (
|
|
|
19
20
|
const openMetadata = {
|
|
20
21
|
name: languageServerOpenTool(id),
|
|
21
22
|
nativeTool: true,
|
|
22
|
-
description: `Start
|
|
23
|
+
description: `Start a ${label} language server instance for this conversation's workspace and load ${rootDescription}. `
|
|
23
24
|
+ `Afterwards every edit or write of a ${extensions} file gets its diagnostics appended automatically, `
|
|
24
|
-
+ `and ${languageServerDiagnosticsTool(id)} is available. Idempotent for the same root;
|
|
25
|
+
+ `and ${languageServerDiagnosticsTool(id)} is available. Idempotent for the same root; other roots stay open.`,
|
|
25
26
|
} as const;
|
|
26
27
|
const diagnosticsMetadata = {
|
|
27
28
|
name: languageServerDiagnosticsTool(id),
|
|
28
29
|
nativeTool: true,
|
|
29
30
|
description: `Current ${label} diagnostics (errors, warnings on request) for ${extensions} files from the running `
|
|
30
|
-
+ `language server, without building. Without paths: all changed files of
|
|
31
|
+
+ `language server, without building. Without paths: all changed files of every open root according to git. `
|
|
32
|
+
+ `With paths every file is answered by the instance whose root contains it; root asks one instance.`,
|
|
33
|
+
} as const;
|
|
34
|
+
const closeMetadata = {
|
|
35
|
+
name: languageServerCloseTool(id),
|
|
36
|
+
nativeTool: true,
|
|
37
|
+
description: `Stop the ${label} language server instance of one root; without root every ${label} instance of `
|
|
38
|
+
+ "this conversation. Other languages and other conversations stay untouched.",
|
|
31
39
|
} as const;
|
|
32
40
|
const openTool = defineRunFunction({
|
|
33
41
|
...openMetadata,
|
|
@@ -51,6 +59,7 @@ export const createLanguageServerToolContributor = (
|
|
|
51
59
|
paths: Type.Optional(Type.Array(Type.String(), {
|
|
52
60
|
description: "Files relative to the workspace root; omit for all changed files",
|
|
53
61
|
})),
|
|
62
|
+
root: Type.Optional(Type.String({ description: "Ask only the instance of this open root" })),
|
|
54
63
|
warnings: Type.Optional(Type.Boolean({ description: "Also list warnings (default: only counted)" })),
|
|
55
64
|
}),
|
|
56
65
|
resultSchema: Type.String(),
|
|
@@ -62,12 +71,28 @@ export const createLanguageServerToolContributor = (
|
|
|
62
71
|
...(scope.signal ? { signal: scope.signal } : {}),
|
|
63
72
|
}) as string,
|
|
64
73
|
});
|
|
74
|
+
const closeTool = defineRunFunction({
|
|
75
|
+
...closeMetadata,
|
|
76
|
+
label: `${label} schließen`,
|
|
77
|
+
schema: Type.Object({
|
|
78
|
+
root: Type.Optional(Type.String({ description: "The open root to stop; omit for every instance of this conversation" })),
|
|
79
|
+
}),
|
|
80
|
+
resultSchema: Type.String(),
|
|
81
|
+
available: alwaysAvailable,
|
|
82
|
+
executionMode: "sequential",
|
|
83
|
+
run: async (scope, toolCallId, input) =>
|
|
84
|
+
await sandbox.execute(scope.caller.runId, closeMetadata.name, input, {
|
|
85
|
+
toolCallId,
|
|
86
|
+
...(scope.signal ? { signal: scope.signal } : {}),
|
|
87
|
+
}) as string,
|
|
88
|
+
});
|
|
65
89
|
return {
|
|
66
90
|
name: `ragents.lsp-${id}`,
|
|
67
91
|
descriptors: [
|
|
68
92
|
toolDescriptorFrom(openMetadata, alwaysAvailable),
|
|
69
93
|
toolDescriptorFrom(diagnosticsMetadata, alwaysAvailable),
|
|
94
|
+
toolDescriptorFrom(closeMetadata, alwaysAvailable),
|
|
70
95
|
],
|
|
71
|
-
tools: () => [openTool, diagnosticsTool],
|
|
96
|
+
tools: () => [openTool, diagnosticsTool, closeTool],
|
|
72
97
|
};
|
|
73
98
|
};
|
|
@@ -6,7 +6,7 @@ import { flag, frontMatterOf, numberOf, tagsOf } from "./front-matter.js";
|
|
|
6
6
|
export type ScriptStartEntry = Extract<StartEntryContribution, { action: "script" }>;
|
|
7
7
|
|
|
8
8
|
const PACKAGE_NAME = /^[a-z0-9][a-z0-9-]*$/;
|
|
9
|
-
const RUN_FIELDS = ["title", "description", "order", "guide", "tags", "coordinator"];
|
|
9
|
+
const RUN_FIELDS = ["title", "description", "order", "guide", "tags", "coordinator", "category"];
|
|
10
10
|
|
|
11
11
|
const requiredFile = (directory: string, name: string): string => {
|
|
12
12
|
const file = path.join(directory, name);
|
|
@@ -45,6 +45,8 @@ const runScriptFrom = (directory: string, name: string, id: string): ScriptStart
|
|
|
45
45
|
const order = numberOf(runFile, "order", fields.get("order"));
|
|
46
46
|
const guide = fields.get("guide");
|
|
47
47
|
const tags = tagsOf(runFile, fields.get("tags"));
|
|
48
|
+
const category = fields.get("category");
|
|
49
|
+
if (category !== undefined && !category.trim()) throw new Error(`${runFile}: category ist leer`);
|
|
48
50
|
const programsDirectory = path.join(directory, "actors");
|
|
49
51
|
const programsStats = statSync(programsDirectory, {throwIfNoEntry: false});
|
|
50
52
|
if (programsStats && !programsStats.isDirectory()) throw new Error(`${programsDirectory} muss ein Verzeichnis sein`);
|
|
@@ -60,6 +62,7 @@ const runScriptFrom = (directory: string, name: string, id: string): ScriptStart
|
|
|
60
62
|
...(order !== undefined ? {order} : {}),
|
|
61
63
|
...(guide ? {guide} : {}),
|
|
62
64
|
...(tags !== undefined ? {tags} : {}),
|
|
65
|
+
...(category ? {category} : {}),
|
|
63
66
|
script: {
|
|
64
67
|
handle: name,
|
|
65
68
|
coordinator: flag(runFile, "coordinator", fields.get("coordinator"), true),
|
|
@@ -13,7 +13,7 @@ export interface StartEntryBase {
|
|
|
13
13
|
/** One entry of the start surface as GET /api/plugins publishes it; a script entry never carries its source. */
|
|
14
14
|
export type StartEntry = StartEntryBase & (
|
|
15
15
|
| { action: "skill"; skill: string; category: string; prompt: string }
|
|
16
|
-
| { action: "script"; coordinator: boolean }
|
|
16
|
+
| { action: "script"; coordinator: boolean; category?: string }
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
export type SkillStartEntry = Extract<StartEntry, { action: "skill" }>;
|
|
@@ -62,9 +62,10 @@ export const startEntryFrom = (value: unknown): StartEntry => {
|
|
|
62
62
|
return { ...base, action: "skill", skill: value.skill, category: value.category, prompt: value.prompt };
|
|
63
63
|
}
|
|
64
64
|
case "script":
|
|
65
|
-
allowed(["coordinator"]);
|
|
65
|
+
allowed(["coordinator", "category"]);
|
|
66
66
|
if (typeof value.coordinator !== "boolean") throw invalid(`${id}: coordinator muss true oder false sein`);
|
|
67
|
-
|
|
67
|
+
if (value.category !== undefined && (!text(value.category) || value.category !== value.category.trim())) throw invalid(`${id}: category muss ein einzelner nicht leerer Text sein`);
|
|
68
|
+
return { ...base, action: "script", coordinator: value.coordinator, ...(value.category !== undefined ? { category: value.category } : {}) };
|
|
68
69
|
default:
|
|
69
70
|
throw invalid(`${id}: unbekannte Aktion ${String(value.action)}; gültig sind skill und script`);
|
|
70
71
|
}
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
type WorkspaceExecutor,
|
|
26
26
|
type WorkspaceProcessContext,
|
|
27
27
|
} from "@aicontainer/workspace-executor";
|
|
28
|
+
import { hostRoot } from "../host-version.js";
|
|
28
29
|
import type { SessionWorkspace } from "../ragents/workspace-runtime.js";
|
|
29
30
|
import { agentToolFrom, toolDescriptorFrom, type AgentToolDefinition } from "./agent-tool.js";
|
|
30
31
|
import { alwaysAvailable } from "./tool-availability.js";
|
|
@@ -137,6 +138,7 @@ export class WorkspaceSandboxHost implements SandboxServices {
|
|
|
137
138
|
root: await workspace.currentRoot(),
|
|
138
139
|
home,
|
|
139
140
|
logDirectory: home.home,
|
|
141
|
+
hostRoot: hostRoot(),
|
|
140
142
|
...(ident ? { ident } : {}),
|
|
141
143
|
additionalRoots: await this.#additionalRoots(runId, ident),
|
|
142
144
|
readOnlyRoots,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { isEnvironmentReference } from "./config-definition.js";
|
|
5
|
+
import { defaultDataDirectory } from "./data-directory.js";
|
|
6
|
+
|
|
7
|
+
export interface ProfileTarget {
|
|
8
|
+
readonly profile: string;
|
|
9
|
+
readonly profileFile: string;
|
|
10
|
+
readonly port: number;
|
|
11
|
+
readonly dataDirectory: string;
|
|
12
|
+
readonly baseUrl: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Port und Datenordner eines Profils, wie der Server sie später selbst bestimmt: Umgebung vor Profildatei vor Vorgabe. */
|
|
16
|
+
export const readProfileTarget = async (profile: string, profileFile: string, environment = process.env): Promise<ProfileTarget> => {
|
|
17
|
+
if (!statSync(profileFile, { throwIfNoEntry: false })?.isFile()) throw new Error(`Die Profildatei fehlt: ${profileFile}`);
|
|
18
|
+
const module = await import(pathToFileURL(profileFile).href) as { config: { host: { PORT?: number; DATA_DIR?: unknown } } };
|
|
19
|
+
const port = Number(environment.PORT || module.config.host.PORT);
|
|
20
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`host.PORT des Profils ${profile} ist keine gültige Portnummer.`);
|
|
21
|
+
const configured = module.config.host.DATA_DIR;
|
|
22
|
+
const fromFile = isEnvironmentReference(configured) ? environment[configured.name] : typeof configured === "string" ? configured : undefined;
|
|
23
|
+
const dataDirectory = path.resolve(environment.DATA_DIR ?? fromFile ?? defaultDataDirectory(profile));
|
|
24
|
+
return { profile, profileFile, port, dataDirectory, baseUrl: `http://localhost:${port}` };
|
|
25
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { statSync } from "node:fs";
|
|
3
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { createInterface } from "node:readline";
|
|
7
|
+
import { readHostVersion } from "./host-version.js";
|
|
8
|
+
|
|
9
|
+
/** Vite kommt im Checkout aus apps/web/node_modules, im Paket aus dessen Abhängigkeiten; beide Male ab apps/web aufgelöst. */
|
|
10
|
+
export const viteEntry = (root: string): string => {
|
|
11
|
+
try {
|
|
12
|
+
const manifest = createRequire(path.join(root, "apps/web/package.json")).resolve("vite/package.json");
|
|
13
|
+
return path.join(path.dirname(manifest), "bin/vite.js");
|
|
14
|
+
} catch (cause) {
|
|
15
|
+
throw new Error(`Der Web-Build findet Vite neben ${path.join(root, "apps/web")} nicht: `
|
|
16
|
+
+ `${cause instanceof Error ? cause.message : String(cause)}`);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const run = (file: string, args: readonly string[], cwd: string, environment: NodeJS.ProcessEnv, log: (line: string) => void): Promise<void> =>
|
|
21
|
+
new Promise((resolve, reject) => {
|
|
22
|
+
const child = spawn(file, args, { cwd, env: environment, stdio: ["ignore", "pipe", "pipe"] });
|
|
23
|
+
for (const stream of [child.stdout, child.stderr]) createInterface({ input: stream! }).on("line", log);
|
|
24
|
+
child.once("error", reject);
|
|
25
|
+
child.once("exit", (code) => code === 0 ? resolve() : reject(new Error(`Der Web-Build endete mit Code ${code}`)));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export interface WebBuild {
|
|
29
|
+
readonly root: string;
|
|
30
|
+
/** Node, das Vite ausführt; in der Erweiterung ist process.execPath Electron und taugt nicht. */
|
|
31
|
+
readonly node?: string;
|
|
32
|
+
readonly outputDirectory: string;
|
|
33
|
+
readonly environment: NodeJS.ProcessEnv;
|
|
34
|
+
readonly log: (line: string) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Baut das Web für den Pluginsatz, den die Umgebung nennt: PRODUCT_PROFILE samt Datei oder PLUGIN_LIST_FILE. */
|
|
38
|
+
export const buildWeb = async (build: WebBuild): Promise<void> => {
|
|
39
|
+
const entry = viteEntry(build.root);
|
|
40
|
+
await rm(build.outputDirectory, { recursive: true, force: true });
|
|
41
|
+
await mkdir(path.dirname(build.outputDirectory), { recursive: true });
|
|
42
|
+
await run(build.node ?? process.execPath, [entry, "build", "--outDir", build.outputDirectory, "--emptyOutDir"], path.join(build.root, "apps/web"), build.environment, build.log);
|
|
43
|
+
if (!statSync(path.join(build.outputDirectory, "index.html"), { throwIfNoEntry: false })?.isFile()) {
|
|
44
|
+
throw new Error(`Der Web-Build hat keine index.html in ${build.outputDirectory} hinterlassen`);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Je Profil ein gebautes Web im Datenordner; der Paketordner bleibt unberührt. */
|
|
49
|
+
export const webBuildDirectory = (dataDirectory: string, profile: string): string => path.join(dataDirectory, "web", profile);
|
|
50
|
+
|
|
51
|
+
export interface WebBuildTarget {
|
|
52
|
+
readonly root: string;
|
|
53
|
+
readonly node?: string;
|
|
54
|
+
readonly profile: string;
|
|
55
|
+
readonly profileFile: string;
|
|
56
|
+
readonly dataDirectory: string;
|
|
57
|
+
readonly environment: NodeJS.ProcessEnv;
|
|
58
|
+
readonly log: (line: string) => void;
|
|
59
|
+
readonly build?: (build: WebBuild) => Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const stampOf = (target: WebBuildTarget): string =>
|
|
63
|
+
JSON.stringify({ root: target.root, profileFile: target.profileFile, hostVersion: readHostVersion(target.root) }, null, 2);
|
|
64
|
+
|
|
65
|
+
/** Baut das Web des Profils, wenn es fehlt oder zuletzt für ein anderes Profil oder eine andere Host-Fassung gebaut wurde. */
|
|
66
|
+
export const ensureWebBuild = async (target: WebBuildTarget): Promise<string> => {
|
|
67
|
+
const directory = webBuildDirectory(target.dataDirectory, target.profile);
|
|
68
|
+
const stampFile = `${directory}.json`;
|
|
69
|
+
const stamp = stampOf(target);
|
|
70
|
+
const built = statSync(path.join(directory, "index.html"), { throwIfNoEntry: false })?.isFile() === true;
|
|
71
|
+
if (built && (await readFile(stampFile, "utf8").catch(() => undefined))?.trim() === stamp) return directory;
|
|
72
|
+
target.log(`== Web des Profils ${target.profile} bauen nach ${directory}`);
|
|
73
|
+
await (target.build ?? buildWeb)({
|
|
74
|
+
root: target.root,
|
|
75
|
+
node: target.node,
|
|
76
|
+
outputDirectory: directory,
|
|
77
|
+
environment: { ...target.environment, PRODUCT_PROFILE: target.profile, PRODUCT_PROFILE_FILE: target.profileFile },
|
|
78
|
+
log: target.log,
|
|
79
|
+
});
|
|
80
|
+
await writeFile(stampFile, `${stamp}\n`);
|
|
81
|
+
return directory;
|
|
82
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2024",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"allowImportingTsExtensions": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"isolatedModules": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"types": [
|
|
11
|
+
"node"
|
|
12
|
+
],
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"lib": [
|
|
15
|
+
"ES2024"
|
|
16
|
+
],
|
|
17
|
+
"paths": {
|
|
18
|
+
"@aicontainer/server/*": [
|
|
19
|
+
"../../apps/server/src/*"
|
|
20
|
+
],
|
|
21
|
+
"@aicontainer/web/*": [
|
|
22
|
+
"../../apps/web/src/*"
|
|
23
|
+
],
|
|
24
|
+
"@aicontainer/plugins/*": [
|
|
25
|
+
"../../plugins/*"
|
|
26
|
+
],
|
|
27
|
+
"@ragents/workflow": [
|
|
28
|
+
"../../plugins/ragents.actor-programs/workflow/index.ts"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"include": [
|
|
33
|
+
"src",
|
|
34
|
+
"../../plugins/*/server",
|
|
35
|
+
"../../plugins/*/contract.ts",
|
|
36
|
+
"../../ragents.config.*.ts"
|
|
37
|
+
]
|
|
38
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="de">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>RAgents Panel</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/column.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="de">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>RAgents</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
package/apps/web/src/App.tsx
CHANGED
|
@@ -30,7 +30,6 @@ export function App() {
|
|
|
30
30
|
const overviewButtonRef = useRef<HTMLButtonElement>(null);
|
|
31
31
|
const settingsButtonRef = useRef<HTMLButtonElement>(null);
|
|
32
32
|
const [runHeaderContainer, setRunHeaderContainer] = useState<HTMLDivElement | null>(null);
|
|
33
|
-
const [canvasToolbarContainer, setCanvasToolbarContainer] = useState<HTMLDivElement | null>(null);
|
|
34
33
|
const [workspaceHeaderContainer, setWorkspaceHeaderContainer] = useState<HTMLDivElement | null>(null);
|
|
35
34
|
const [statusContainer, setStatusContainer] = useState<HTMLElement | null>(null);
|
|
36
35
|
const [workspaceModalContainer, setWorkspaceModalContainer] = useState<HTMLDivElement | null>(null);
|
|
@@ -80,13 +79,6 @@ export function App() {
|
|
|
80
79
|
if (restoreFocus) overviewButtonRef.current?.focus({ preventScroll: true });
|
|
81
80
|
}, []);
|
|
82
81
|
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
if (!canvasToolbarContainer) return;
|
|
85
|
-
const activateCanvas = () => { closeOverview(); setToolbarPanel(undefined); };
|
|
86
|
-
canvasToolbarContainer.addEventListener("click", activateCanvas);
|
|
87
|
-
return () => canvasToolbarContainer.removeEventListener("click", activateCanvas);
|
|
88
|
-
}, [canvasToolbarContainer, closeOverview]);
|
|
89
|
-
|
|
90
82
|
// Overview contributions keep their state after the first opening.
|
|
91
83
|
const openOverview = useCallback(() => {
|
|
92
84
|
setToolbarPanel(undefined);
|
|
@@ -202,7 +194,6 @@ export function App() {
|
|
|
202
194
|
{busy ? <Spinner aria-hidden className="size-[18px]" /> : <SparklesIcon size={22} />}
|
|
203
195
|
</button>
|
|
204
196
|
</div>}
|
|
205
|
-
<div className="relative flex flex-none self-stretch empty:hidden" ref={setCanvasToolbarContainer} />
|
|
206
197
|
{toolbarPanels.map(({ id, Panel }) => <ToolbarPanelHost
|
|
207
198
|
id={id} key={id} onBusy={setPanelBusy}
|
|
208
199
|
onClose={() => setToolbarPanel(undefined)}
|
|
@@ -230,7 +221,6 @@ export function App() {
|
|
|
230
221
|
{readRuns && activeId
|
|
231
222
|
? (
|
|
232
223
|
<PluginChat
|
|
233
|
-
canvasToolbarContainer={canvasToolbarContainer}
|
|
234
224
|
headerContainer={runHeaderContainer}
|
|
235
225
|
workspaceHeaderContainer={workspaceHeaderContainer}
|
|
236
226
|
statusContainer={statusContainer}
|
|
@@ -55,7 +55,7 @@ export type ChatLayout = "workspace" | "column" | { element: string };
|
|
|
55
55
|
|
|
56
56
|
interface PluginChatProps {
|
|
57
57
|
layout?: ChatLayout;
|
|
58
|
-
|
|
58
|
+
toolbarContainer?: HTMLElement | null;
|
|
59
59
|
statusContainer?: HTMLElement | null;
|
|
60
60
|
headerContainer?: HTMLElement | null;
|
|
61
61
|
workspaceHeaderContainer?: HTMLElement | null;
|
|
@@ -71,7 +71,7 @@ interface PluginChatProps {
|
|
|
71
71
|
|
|
72
72
|
interface ChatWorkspaceProps {
|
|
73
73
|
layout: ChatLayout;
|
|
74
|
-
|
|
74
|
+
toolbarContainer?: HTMLElement | null;
|
|
75
75
|
statusContainer?: HTMLElement | null;
|
|
76
76
|
panelState: WorkspacePanelState;
|
|
77
77
|
onTogglePanel: () => void;
|
|
@@ -90,7 +90,7 @@ interface ChatWorkspaceProps {
|
|
|
90
90
|
tabs: readonly WorkspaceTabContribution[];
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
export function PluginChat({ layout = "workspace", headerContainer,
|
|
93
|
+
export function PluginChat({ layout = "workspace", headerContainer, toolbarContainer, workspaceHeaderContainer, statusContainer, initialStartOptions, onStarted, onLocationChange, onViewed, viewing = false, registry, session, startDialog }: PluginChatProps) {
|
|
94
94
|
const access = useAccess();
|
|
95
95
|
const startOnly = startDialog !== undefined;
|
|
96
96
|
const [modalContainer, setModalContainer] = useState<HTMLDivElement | null>(null);
|
|
@@ -235,7 +235,7 @@ export function PluginChat({ layout = "workspace", headerContainer, canvasToolba
|
|
|
235
235
|
panelVisible={panelState === "expanded"} runId={session.id} tabs={availableTabs} />}
|
|
236
236
|
<ChatWorkspace
|
|
237
237
|
layout={layout}
|
|
238
|
-
|
|
238
|
+
toolbarContainer={toolbarContainer}
|
|
239
239
|
panelState={panelState}
|
|
240
240
|
onTogglePanel={() => updatePanelState(panelState === "expanded" ? "collapsed" : "expanded")}
|
|
241
241
|
headerContainer={headerContainer}
|
|
@@ -281,7 +281,7 @@ function UserLocationReporter({ activeTabId, onChange, panelVisible, runId, tabs
|
|
|
281
281
|
|
|
282
282
|
function ChatWorkspace({
|
|
283
283
|
layout,
|
|
284
|
-
|
|
284
|
+
toolbarContainer = null,
|
|
285
285
|
panelState,
|
|
286
286
|
onTogglePanel,
|
|
287
287
|
headerContainer,
|
|
@@ -385,7 +385,7 @@ function ChatWorkspace({
|
|
|
385
385
|
? <CanvasElementView elementId={layout.element} navigation={navigation} registry={registry} session={session} />
|
|
386
386
|
: CanvasColumn
|
|
387
387
|
? <CanvasColumn
|
|
388
|
-
toolbarContainer={
|
|
388
|
+
toolbarContainer={toolbarContainer}
|
|
389
389
|
runToolbarContainer={null}
|
|
390
390
|
statusContainer={statusContainer}
|
|
391
391
|
cardSections={registry.cardSections}
|
|
@@ -420,7 +420,7 @@ function ChatWorkspace({
|
|
|
420
420
|
{CanvasCenter
|
|
421
421
|
? (
|
|
422
422
|
<CanvasCenter
|
|
423
|
-
toolbarContainer={
|
|
423
|
+
toolbarContainer={toolbarContainer}
|
|
424
424
|
runToolbarContainer={runToolbarContainer}
|
|
425
425
|
statusContainer={statusContainer}
|
|
426
426
|
cardSections={registry.cardSections}
|
|
@@ -477,7 +477,7 @@ function CanvasElementView({ elementId, navigation, registry, session }: {
|
|
|
477
477
|
{session.runView === undefined ? "Der Run wird geladen ..." : "Diese Mini-App gibt es in dem Run nicht mehr."}
|
|
478
478
|
</div>;
|
|
479
479
|
return <div className="flex min-h-0 min-w-0 flex-1 flex-col" data-view="element">
|
|
480
|
-
<match.Element definition={match.definition} navigation={navigation}
|
|
480
|
+
<match.Element definition={match.definition} navigation={navigation} session={session} />
|
|
481
481
|
</div>;
|
|
482
482
|
}
|
|
483
483
|
|
|
@@ -152,25 +152,13 @@ export interface CanvasElementDefinition {
|
|
|
152
152
|
id: string;
|
|
153
153
|
visible?: boolean;
|
|
154
154
|
title?: string;
|
|
155
|
-
width: number;
|
|
156
|
-
height: number;
|
|
157
|
-
collapsedHeight?: number;
|
|
158
155
|
anchorActorId?: string;
|
|
159
156
|
entity?: EntityReference;
|
|
160
|
-
resizable?: {
|
|
161
|
-
minWidth: number;
|
|
162
|
-
maxWidth: number;
|
|
163
|
-
minHeight: number;
|
|
164
|
-
maxHeight: number;
|
|
165
|
-
};
|
|
166
157
|
data?: unknown;
|
|
167
158
|
}
|
|
168
159
|
|
|
169
160
|
export interface CanvasElementContext {
|
|
170
161
|
definition: CanvasElementDefinition;
|
|
171
|
-
presentation?: "canvas" | "tiled";
|
|
172
|
-
collapsed?: boolean;
|
|
173
|
-
onCollapsedChange?: (collapsed: boolean) => void;
|
|
174
162
|
navigation: SessionNavigation;
|
|
175
163
|
session: SessionContext;
|
|
176
164
|
}
|
|
@@ -120,13 +120,13 @@ export function StartEntryLibrary({ skills, scripts, launchDisabled, onOpen, has
|
|
|
120
120
|
const sorted = useMemo(() => [...[...skills].sort(byOrder), ...[...scripts].sort(byOrder)], [skills, scripts]);
|
|
121
121
|
const tags = [...new Set(sorted.flatMap((entry) => entry.tags ?? []))].sort((a, b) => a.localeCompare(b, "de-DE"));
|
|
122
122
|
const filtered = sorted.filter((entry) => (!tag || entry.tags?.includes(tag))
|
|
123
|
-
&& `${entry.title} ${entry.description} ${entry.
|
|
123
|
+
&& `${entry.title} ${entry.description} ${entry.category ?? kindLabel(entry)} ${entry.owner} ${(entry.tags ?? []).join(" ")}`
|
|
124
124
|
.toLocaleLowerCase("de-DE").includes(query.trim().toLocaleLowerCase("de-DE")));
|
|
125
125
|
const selected = filtered.find((entry) => entry.id === selectedId) ?? filtered[0];
|
|
126
126
|
const guided = selected && hasGuide(selected);
|
|
127
127
|
const tagOptions = [{ value: "", label: "Alle Schlagworte" }, ...tags.map((value) => ({ value, label: value }))];
|
|
128
128
|
const items: ListDetailItem[] = filtered.map((entry) => ({ id: entry.id, title: entry.title,
|
|
129
|
-
description: entry.description, group: entry.
|
|
129
|
+
description: entry.description, group: entry.category ?? (technical ? "Run-Scripts" : "Abläufe"),
|
|
130
130
|
icon: entryIcon(entry), meta: technical ? kindLabel(entry) : undefined, tone: entry.action === "skill" ? "purple" : "success" }));
|
|
131
131
|
|
|
132
132
|
return <ListDetail className="min-h-0 flex-1" label="Skills und Abläufe" detailLabel="Vorschau" items={items}
|
|
@@ -25,7 +25,7 @@ export function ThemeSettings() {
|
|
|
25
25
|
</div>
|
|
26
26
|
<div className="flex-[1_1_220px]">
|
|
27
27
|
<h3 className="mb-1.5 text-lg font-semibold" id="theme-settings-title">Schichtwerk</h3>
|
|
28
|
-
<p className={noteClasses}>Matte Flächen
|
|
28
|
+
<p className={noteClasses}>Matte Flächen und gezeichnete Konturen.</p>
|
|
29
29
|
</div>
|
|
30
30
|
<ToggleGroup aria-label="Schichtwerk-Farbschema" className="flex-none max-sm:w-full" disabled={!writable} size="sm" spacing={0}
|
|
31
31
|
value={[preference]} variant="outline"
|