chore: rename plugin for multi-session scope
This commit is contained in:
parent
aa0c2323ea
commit
b9c05e3657
26
README.md
26
README.md
@ -1,14 +1,14 @@
|
||||
# xworkmate-artifacts
|
||||
# openclaw-multi-session-plugins
|
||||
|
||||
OpenClaw Gateway plugin that exports structured workspace artifact manifests for XWorkmate.
|
||||
OpenClaw plugin for logical multi-session isolation and scoped XWorkmate artifact manifests.
|
||||
|
||||
## Why
|
||||
|
||||
XWorkmate talks to OpenClaw through `xworkmate-bridge` using the existing
|
||||
`/gateway/openclaw` task contract. The bridge sends `chat.send`, waits for
|
||||
`agent.wait`, then asks this plugin for a structured artifact manifest. The APP
|
||||
can then sync generated files into its local thread workspace without changing
|
||||
the UI or adding provider-specific routes.
|
||||
`agent.wait`, then asks this plugin for a session/run-scoped artifact manifest.
|
||||
The APP can then sync generated files into its local thread workspace without
|
||||
changing the UI or adding provider-specific routes.
|
||||
|
||||
It registers four Gateway methods:
|
||||
|
||||
@ -28,16 +28,16 @@ into the APP `artifacts[]` contract.
|
||||
Install from the npm package through OpenClaw:
|
||||
|
||||
```bash
|
||||
openclaw plugins install xworkmate-artifacts
|
||||
openclaw plugins enable xworkmate-artifacts
|
||||
openclaw plugins install openclaw-multi-session-plugins
|
||||
openclaw plugins enable openclaw-multi-session-plugins
|
||||
```
|
||||
|
||||
Or install from a Git checkout for development:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/x-evor/xworkmate-artifacts.git
|
||||
openclaw plugins install --link ./xworkmate-artifacts
|
||||
openclaw plugins enable xworkmate-artifacts
|
||||
git clone https://github.com/x-evor/openclaw-multi-session-plugins.git
|
||||
openclaw plugins install --link ./openclaw-multi-session-plugins
|
||||
openclaw plugins enable openclaw-multi-session-plugins
|
||||
```
|
||||
|
||||
Equivalent config shape for a linked checkout:
|
||||
@ -47,11 +47,11 @@ Equivalent config shape for a linked checkout:
|
||||
"plugins": {
|
||||
"load": {
|
||||
"paths": [
|
||||
"/path/to/xworkmate-artifacts"
|
||||
"/path/to/openclaw-multi-session-plugins"
|
||||
]
|
||||
},
|
||||
"entries": {
|
||||
"xworkmate-artifacts": {
|
||||
"openclaw-multi-session-plugins": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
@ -151,7 +151,7 @@ show a quick artifact table:
|
||||
{
|
||||
"id": "main",
|
||||
"tools": {
|
||||
"allow": ["xworkmate_artifacts"]
|
||||
"allow": ["openclaw_multi_session_artifacts"]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
12
dist/index.js
vendored
12
dist/index.js
vendored
@ -1,8 +1,8 @@
|
||||
import { exportXWorkmateArtifacts, prepareXWorkmateArtifacts, readXWorkmateArtifact, } from "./src/exportArtifacts.js";
|
||||
const plugin = {
|
||||
id: "xworkmate-artifacts",
|
||||
name: "XWorkmate Artifacts",
|
||||
description: "Exports structured artifact manifests from the OpenClaw workspace for XWorkmate.",
|
||||
id: "openclaw-multi-session-plugins",
|
||||
name: "openclaw-multi-session-plugins",
|
||||
description: "OpenClaw logical isolation support for multi-session plugin runtimes and scoped XWorkmate artifacts.",
|
||||
register,
|
||||
};
|
||||
export default plugin;
|
||||
@ -72,14 +72,14 @@ function register(api) {
|
||||
}
|
||||
});
|
||||
api.registerTool((ctx) => createXWorkmateArtifactsTool(api, ctx), {
|
||||
names: ["xworkmate_artifacts"],
|
||||
names: ["openclaw_multi_session_artifacts"],
|
||||
optional: true,
|
||||
});
|
||||
}
|
||||
function createXWorkmateArtifactsTool(api, ctx) {
|
||||
return {
|
||||
name: "xworkmate_artifacts",
|
||||
label: "XWorkmate Artifacts",
|
||||
name: "openclaw_multi_session_artifacts",
|
||||
label: "openclaw-multi-session-plugins",
|
||||
description: "List generated artifacts in the current OpenClaw workspace or read one small artifact as base64 for XWorkmate.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
|
||||
@ -12,7 +12,7 @@ describe("plugin registration", () => {
|
||||
configSchema?: { properties?: Record<string, unknown> };
|
||||
};
|
||||
|
||||
expect(manifest.contracts?.tools).toContain("xworkmate_artifacts");
|
||||
expect(manifest.contracts?.tools).toContain("openclaw_multi_session_artifacts");
|
||||
expect(manifest.configSchema?.properties?.artifactRefSigningSecret).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
12
index.ts
12
index.ts
@ -16,9 +16,9 @@ type XWorkmateToolContext = {
|
||||
};
|
||||
|
||||
const plugin = {
|
||||
id: "xworkmate-artifacts",
|
||||
name: "XWorkmate Artifacts",
|
||||
description: "Exports structured artifact manifests from the OpenClaw workspace for XWorkmate.",
|
||||
id: "openclaw-multi-session-plugins",
|
||||
name: "openclaw-multi-session-plugins",
|
||||
description: "OpenClaw logical isolation support for multi-session plugin runtimes and scoped XWorkmate artifacts.",
|
||||
register,
|
||||
};
|
||||
|
||||
@ -86,7 +86,7 @@ function register(api: OpenClawPluginApi) {
|
||||
}
|
||||
});
|
||||
api.registerTool((ctx) => createXWorkmateArtifactsTool(api, ctx), {
|
||||
names: ["xworkmate_artifacts"],
|
||||
names: ["openclaw_multi_session_artifacts"],
|
||||
optional: true,
|
||||
});
|
||||
}
|
||||
@ -96,8 +96,8 @@ function createXWorkmateArtifactsTool(
|
||||
ctx: XWorkmateToolContext,
|
||||
): AnyAgentTool {
|
||||
return {
|
||||
name: "xworkmate_artifacts",
|
||||
label: "XWorkmate Artifacts",
|
||||
name: "openclaw_multi_session_artifacts",
|
||||
label: "openclaw-multi-session-plugins",
|
||||
description:
|
||||
"List generated artifacts in the current OpenClaw workspace or read one small artifact as base64 for XWorkmate.",
|
||||
parameters: {
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
{
|
||||
"id": "xworkmate-artifacts",
|
||||
"name": "XWorkmate Artifacts",
|
||||
"description": "Exports structured artifact manifests from the OpenClaw workspace for XWorkmate.",
|
||||
"id": "openclaw-multi-session-plugins",
|
||||
"name": "openclaw-multi-session-plugins",
|
||||
"description": "OpenClaw logical isolation support for multi-session plugin runtimes and scoped XWorkmate artifacts.",
|
||||
"activation": {
|
||||
"onStartup": true
|
||||
},
|
||||
"contracts": {
|
||||
"tools": ["xworkmate_artifacts"]
|
||||
"tools": ["openclaw_multi_session_artifacts"]
|
||||
},
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
|
||||
15
package.json
15
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "xworkmate-artifacts",
|
||||
"name": "openclaw-multi-session-plugins",
|
||||
"version": "0.1.8",
|
||||
"description": "XWorkmate artifact export plugin for OpenClaw Gateway",
|
||||
"description": "OpenClaw multi-session plugin runtime support for scoped XWorkmate artifacts",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@ -9,15 +9,18 @@
|
||||
"xworkmate",
|
||||
"artifacts",
|
||||
"gateway",
|
||||
"plugin"
|
||||
"plugin",
|
||||
"multi-session",
|
||||
"session-isolation",
|
||||
"logical-isolation"
|
||||
],
|
||||
"homepage": "https://github.com/x-evor/xworkmate-artifacts#readme",
|
||||
"homepage": "https://github.com/x-evor/openclaw-multi-session-plugins#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/x-evor/xworkmate-artifacts.git"
|
||||
"url": "git+https://github.com/x-evor/openclaw-multi-session-plugins.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/x-evor/xworkmate-artifacts/issues"
|
||||
"url": "https://github.com/x-evor/openclaw-multi-session-plugins/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
|
||||
describe("exportXWorkmateArtifacts", () => {
|
||||
it("prepares isolated task artifact scopes", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
|
||||
const first = await prepareXWorkmateArtifacts({
|
||||
params: { sessionKey: "thread-main", runId: "turn-1" },
|
||||
@ -31,7 +31,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("exports changed files with metadata and base64 content", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
await fs.mkdir(path.join(root, "reports"), { recursive: true });
|
||||
const filePath = path.join(root, "reports", "final.md");
|
||||
await fs.writeFile(filePath, "# Done\n");
|
||||
@ -64,7 +64,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("filters old files by sinceUnixMs", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
const oldFile = path.join(root, "old.txt");
|
||||
await fs.writeFile(oldFile, "old");
|
||||
const stat = await fs.stat(oldFile);
|
||||
@ -82,7 +82,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("skips excluded directories and symlinks", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
await fs.mkdir(path.join(root, ".git"), { recursive: true });
|
||||
await fs.mkdir(path.join(root, ".xworkmate", "artifacts"), { recursive: true });
|
||||
await fs.writeFile(path.join(root, ".git", "secret.txt"), "secret");
|
||||
@ -103,7 +103,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("exports only files inside a task artifact scope", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
const first = await prepareXWorkmateArtifacts({
|
||||
params: { sessionKey: "thread-main", runId: "turn-1" },
|
||||
pluginConfig: { workspaceDir: root },
|
||||
@ -136,7 +136,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("falls back to latest workspace files when the scoped directory is empty", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
const prepared = await prepareXWorkmateArtifacts({
|
||||
params: { sessionKey: "thread-main", runId: "turn-1" },
|
||||
pluginConfig: { workspaceDir: root },
|
||||
@ -172,7 +172,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("falls back to latest session task files when requested", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
const previousTask = await prepareXWorkmateArtifacts({
|
||||
params: { sessionKey: "thread-main", runId: "turn-previous" },
|
||||
pluginConfig: { workspaceDir: root },
|
||||
@ -210,7 +210,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("leaves oversized artifacts out of inline content", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
await fs.writeFile(path.join(root, "large.pdf"), Buffer.from("large-content"));
|
||||
|
||||
const result = await exportXWorkmateArtifacts({
|
||||
@ -229,7 +229,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("can list artifacts without inline content", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
await fs.writeFile(path.join(root, "small.txt"), "small");
|
||||
|
||||
const result = await exportXWorkmateArtifacts({
|
||||
@ -248,7 +248,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("limits exported files", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
await fs.writeFile(path.join(root, "a.txt"), "a");
|
||||
await fs.writeFile(path.join(root, "b.txt"), "b");
|
||||
|
||||
@ -266,8 +266,8 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("selects an agent workspace from agent session keys", async () => {
|
||||
const mainRoot = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-main-"));
|
||||
const agentRoot = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-agent-"));
|
||||
const mainRoot = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-main-"));
|
||||
const agentRoot = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-agent-"));
|
||||
await fs.writeFile(path.join(mainRoot, "main.txt"), "main");
|
||||
await fs.writeFile(path.join(agentRoot, "agent.txt"), "agent");
|
||||
|
||||
@ -288,7 +288,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("rejects unscoped artifact reads by relative path", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
await fs.mkdir(path.join(root, "reports"), { recursive: true });
|
||||
await fs.writeFile(path.join(root, "reports", "final.txt"), "final");
|
||||
|
||||
@ -305,7 +305,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("reads one artifact inside a task artifact scope", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
const prepared = await prepareXWorkmateArtifacts({
|
||||
params: { sessionKey: "thread-main", runId: "turn-1" },
|
||||
pluginConfig: { workspaceDir: root },
|
||||
@ -336,7 +336,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("reads a latest workspace artifact only through its artifactRef", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
const prepared = await prepareXWorkmateArtifacts({
|
||||
params: { sessionKey: "thread-main", runId: "turn-1" },
|
||||
pluginConfig: { workspaceDir: root },
|
||||
@ -374,7 +374,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("rejects tampered artifact refs", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
await fs.writeFile(path.join(root, "existing.txt"), "existing");
|
||||
const exported = await exportXWorkmateArtifacts({
|
||||
params: {
|
||||
@ -399,7 +399,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("reads artifact metadata without inline content when the file exceeds the limit", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
const prepared = await prepareXWorkmateArtifacts({
|
||||
params: { sessionKey: "thread-main", runId: "turn-1" },
|
||||
pluginConfig: { workspaceDir: root },
|
||||
@ -430,7 +430,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("rejects relative path traversal when reading artifacts", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
const prepared = await prepareXWorkmateArtifacts({
|
||||
params: { sessionKey: "thread-main", runId: "turn-1" },
|
||||
pluginConfig: { workspaceDir: root },
|
||||
@ -450,7 +450,7 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("rejects artifact scope traversal when reading artifacts", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
|
||||
await expect(
|
||||
readXWorkmateArtifact({
|
||||
@ -466,8 +466,8 @@ describe("exportXWorkmateArtifacts", () => {
|
||||
});
|
||||
|
||||
it("rejects symlink escapes when reading artifacts", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-artifacts-"));
|
||||
const outsideRoot = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-xworkmate-outside-"));
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-"));
|
||||
const outsideRoot = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-outside-"));
|
||||
const outsideFile = path.join(outsideRoot, "secret.txt");
|
||||
await fs.writeFile(outsideFile, "secret");
|
||||
const prepared = await prepareXWorkmateArtifacts({
|
||||
|
||||
Loading…
Reference in New Issue
Block a user