Clarify unprepared artifact scopes
This commit is contained in:
parent
0ee969ea16
commit
5c686f95b2
29
dist/src/exportArtifacts.js
vendored
29
dist/src/exportArtifacts.js
vendored
@ -76,13 +76,19 @@ export async function exportXWorkmateArtifacts(input) {
|
||||
const artifactScope = requestedArtifactScope || expectedArtifactScope;
|
||||
const scopeRoot = resolveScopeRoot(workspaceRoot, artifactScope);
|
||||
const scopeKind = "task";
|
||||
const candidates = await collectCandidates({
|
||||
scanRoot: scopeRoot,
|
||||
relativeRoot: scopeRoot,
|
||||
sinceUnixMs,
|
||||
skipTaskScopeRoot: false,
|
||||
warnings,
|
||||
});
|
||||
const scopePrepared = await directoryExists(scopeRoot);
|
||||
const candidates = scopePrepared
|
||||
? await collectCandidates({
|
||||
scanRoot: scopeRoot,
|
||||
relativeRoot: scopeRoot,
|
||||
sinceUnixMs,
|
||||
skipTaskScopeRoot: false,
|
||||
warnings,
|
||||
})
|
||||
: [];
|
||||
if (!scopePrepared) {
|
||||
warnings.push("artifact scope is not prepared");
|
||||
}
|
||||
candidates.sort((left, right) => {
|
||||
if (right.mtimeMs !== left.mtimeMs) {
|
||||
return right.mtimeMs - left.mtimeMs;
|
||||
@ -397,6 +403,15 @@ function safeTaskSessionScope(value) {
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
async function directoryExists(absolutePath) {
|
||||
try {
|
||||
const stat = await fs.stat(absolutePath);
|
||||
return stat.isDirectory();
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function safeArtifactRefRunScope(value) {
|
||||
try {
|
||||
return safeTaskArtifactScope(value);
|
||||
|
||||
@ -7,6 +7,11 @@ import plugin from "./index.js";
|
||||
import { prepareXWorkmateArtifacts } from "./src/exportArtifacts.js";
|
||||
|
||||
type GatewayMethodHandler = Parameters<OpenClawPluginApi["registerGatewayMethod"]>[1];
|
||||
type GatewayMethodResponse = {
|
||||
ok: boolean;
|
||||
payload?: Record<string, unknown>;
|
||||
error?: { code?: string; message?: string };
|
||||
};
|
||||
|
||||
describe("plugin registration", () => {
|
||||
it("declares registered agent tools in the manifest contract", () => {
|
||||
@ -50,6 +55,68 @@ describe("plugin registration", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("executes registered gateway methods against the current task scope", async () => {
|
||||
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-gateway-"));
|
||||
const methods = new Map<string, GatewayMethodHandler>();
|
||||
const api = {
|
||||
config: {},
|
||||
pluginConfig: { workspaceDir: root },
|
||||
registerGatewayMethod: (method: string, handler: GatewayMethodHandler) => {
|
||||
methods.set(method, handler);
|
||||
},
|
||||
registerTool: () => undefined,
|
||||
} as unknown as OpenClawPluginApi;
|
||||
|
||||
plugin.register(api);
|
||||
|
||||
const prepared = await callGatewayMethod(methods, "xworkmate.artifacts.prepare", {
|
||||
sessionKey: "thread-main",
|
||||
runId: "turn-1",
|
||||
});
|
||||
expect(prepared.ok).toBe(true);
|
||||
expect(prepared.payload?.artifactScope).toBe("tasks/thread-main/turn-1");
|
||||
const artifactDirectory = String(prepared.payload?.artifactDirectory);
|
||||
|
||||
const emptyExport = await callGatewayMethod(methods, "xworkmate.artifacts.export", {
|
||||
sessionKey: "thread-main",
|
||||
runId: "turn-1",
|
||||
artifactScope: prepared.payload?.artifactScope,
|
||||
});
|
||||
expect(emptyExport.ok).toBe(true);
|
||||
expect(emptyExport.payload?.artifacts).toEqual([]);
|
||||
expect(emptyExport.payload?.warnings).toEqual([]);
|
||||
|
||||
await fs.promises.mkdir(path.join(artifactDirectory, "reports"), { recursive: true });
|
||||
await fs.promises.writeFile(path.join(artifactDirectory, "reports", "final.md"), "final");
|
||||
|
||||
const listed = await callGatewayMethod(methods, "xworkmate.artifacts.list", {
|
||||
sessionKey: "thread-main",
|
||||
runId: "turn-1",
|
||||
artifactScope: prepared.payload?.artifactScope,
|
||||
});
|
||||
expect(listed.ok).toBe(true);
|
||||
expect(listed.payload?.artifacts).toMatchObject([{ relativePath: "reports/final.md" }]);
|
||||
const listedArtifacts = listed.payload?.artifacts as Array<Record<string, unknown>>;
|
||||
expect(listedArtifacts[0]).not.toHaveProperty("content");
|
||||
|
||||
const read = await callGatewayMethod(methods, "xworkmate.artifacts.read", {
|
||||
sessionKey: "thread-main",
|
||||
runId: "turn-1",
|
||||
artifactScope: prepared.payload?.artifactScope,
|
||||
relativePath: "reports/final.md",
|
||||
});
|
||||
expect(read.ok).toBe(true);
|
||||
expect(read.payload?.artifacts).toMatchObject([{ relativePath: "reports/final.md", encoding: "base64" }]);
|
||||
|
||||
const unprepared = await callGatewayMethod(methods, "xworkmate.artifacts.export", {
|
||||
sessionKey: "thread-main",
|
||||
runId: "turn-unprepared",
|
||||
});
|
||||
expect(unprepared.ok).toBe(true);
|
||||
expect(unprepared.payload?.artifacts).toEqual([]);
|
||||
expect(unprepared.payload?.warnings).toEqual(["artifact scope is not prepared"]);
|
||||
});
|
||||
|
||||
it("does not invent default session or run ids for the optional agent tool", async () => {
|
||||
const tools: Array<{ tool: unknown; options: unknown }> = [];
|
||||
const api = {
|
||||
@ -120,3 +187,25 @@ describe("plugin registration", () => {
|
||||
expect(result.content[0]?.text).not.toContain("global.txt");
|
||||
});
|
||||
});
|
||||
|
||||
async function callGatewayMethod(
|
||||
methods: Map<string, GatewayMethodHandler>,
|
||||
method: string,
|
||||
params: Record<string, unknown>,
|
||||
): Promise<GatewayMethodResponse> {
|
||||
const handler = methods.get(method);
|
||||
if (!handler) {
|
||||
throw new Error(`missing gateway method ${method}`);
|
||||
}
|
||||
let response: GatewayMethodResponse | undefined;
|
||||
await handler({
|
||||
params,
|
||||
respond: (ok: boolean, payload?: Record<string, unknown>, error?: GatewayMethodResponse["error"]) => {
|
||||
response = { ok, payload, error };
|
||||
},
|
||||
} as Parameters<GatewayMethodHandler>[0]);
|
||||
if (!response) {
|
||||
throw new Error(`gateway method ${method} did not respond`);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@ -157,13 +157,19 @@ export async function exportXWorkmateArtifacts(input: ExportInput): Promise<XWor
|
||||
const artifactScope = requestedArtifactScope || expectedArtifactScope;
|
||||
const scopeRoot = resolveScopeRoot(workspaceRoot, artifactScope);
|
||||
const scopeKind: XWorkmateArtifactScopeKind = "task";
|
||||
const candidates = await collectCandidates({
|
||||
scanRoot: scopeRoot,
|
||||
relativeRoot: scopeRoot,
|
||||
sinceUnixMs,
|
||||
skipTaskScopeRoot: false,
|
||||
warnings,
|
||||
});
|
||||
const scopePrepared = await directoryExists(scopeRoot);
|
||||
const candidates = scopePrepared
|
||||
? await collectCandidates({
|
||||
scanRoot: scopeRoot,
|
||||
relativeRoot: scopeRoot,
|
||||
sinceUnixMs,
|
||||
skipTaskScopeRoot: false,
|
||||
warnings,
|
||||
})
|
||||
: [];
|
||||
if (!scopePrepared) {
|
||||
warnings.push("artifact scope is not prepared");
|
||||
}
|
||||
|
||||
candidates.sort((left, right) => {
|
||||
if (right.mtimeMs !== left.mtimeMs) {
|
||||
@ -523,6 +529,15 @@ function safeTaskSessionScope(value: unknown): string {
|
||||
return scope;
|
||||
}
|
||||
|
||||
async function directoryExists(absolutePath: string): Promise<boolean> {
|
||||
try {
|
||||
const stat = await fs.stat(absolutePath);
|
||||
return stat.isDirectory();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function safeArtifactRefRunScope(value: unknown): string {
|
||||
try {
|
||||
return safeTaskArtifactScope(value);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user