palmier 0.9.6 → 0.9.8
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 +28 -13
- package/dist/agents/agent.d.ts +0 -1
- package/dist/agents/agent.js +0 -1
- package/dist/agents/aider.d.ts +0 -1
- package/dist/agents/aider.js +0 -1
- package/dist/agents/claude.d.ts +0 -1
- package/dist/agents/claude.js +0 -1
- package/dist/agents/cline.d.ts +0 -1
- package/dist/agents/cline.js +0 -1
- package/dist/agents/codex.d.ts +0 -1
- package/dist/agents/codex.js +0 -1
- package/dist/agents/copilot.d.ts +0 -1
- package/dist/agents/copilot.js +0 -1
- package/dist/agents/cursor.d.ts +0 -1
- package/dist/agents/cursor.js +0 -1
- package/dist/agents/deepagents.d.ts +0 -1
- package/dist/agents/deepagents.js +0 -1
- package/dist/agents/droid.d.ts +0 -1
- package/dist/agents/droid.js +0 -1
- package/dist/agents/gemini.d.ts +0 -1
- package/dist/agents/gemini.js +0 -1
- package/dist/agents/goose.d.ts +0 -1
- package/dist/agents/goose.js +0 -1
- package/dist/agents/hermes.d.ts +0 -1
- package/dist/agents/hermes.js +0 -1
- package/dist/agents/kimi.d.ts +0 -1
- package/dist/agents/kimi.js +0 -1
- package/dist/agents/kiro.d.ts +0 -1
- package/dist/agents/kiro.js +0 -1
- package/dist/agents/openclaw.d.ts +0 -1
- package/dist/agents/openclaw.js +0 -1
- package/dist/agents/opencode.d.ts +0 -1
- package/dist/agents/opencode.js +0 -1
- package/dist/agents/qoder.d.ts +0 -1
- package/dist/agents/qoder.js +0 -1
- package/dist/agents/qwen.d.ts +0 -1
- package/dist/agents/qwen.js +0 -1
- package/dist/agents/shared-prompt.d.ts +0 -1
- package/dist/agents/shared-prompt.js +0 -1
- package/dist/client-store.d.ts +0 -1
- package/dist/client-store.js +0 -1
- package/dist/commands/clients.d.ts +0 -1
- package/dist/commands/clients.js +0 -1
- package/dist/commands/info.d.ts +0 -1
- package/dist/commands/info.js +0 -1
- package/dist/commands/init.d.ts +0 -1
- package/dist/commands/init.js +1 -2
- package/dist/commands/pair.d.ts +0 -1
- package/dist/commands/pair.js +0 -1
- package/dist/commands/restart.d.ts +0 -1
- package/dist/commands/restart.js +0 -1
- package/dist/commands/run.d.ts +0 -1
- package/dist/commands/run.js +19 -3
- package/dist/commands/serve.d.ts +0 -1
- package/dist/commands/serve.js +0 -1
- package/dist/commands/uninstall.d.ts +0 -1
- package/dist/commands/uninstall.js +0 -1
- package/dist/config.d.ts +0 -1
- package/dist/config.js +0 -1
- package/dist/event-queues.d.ts +0 -1
- package/dist/event-queues.js +0 -1
- package/dist/events.d.ts +0 -1
- package/dist/events.js +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/linked-device.d.ts +0 -1
- package/dist/linked-device.js +0 -1
- package/dist/mcp-handler.d.ts +0 -1
- package/dist/mcp-handler.js +0 -1
- package/dist/mcp-tools.d.ts +0 -1
- package/dist/mcp-tools.js +0 -1
- package/dist/nats-client.d.ts +0 -1
- package/dist/nats-client.js +0 -1
- package/dist/network.d.ts +0 -1
- package/dist/network.js +0 -1
- package/dist/notification-store.d.ts +0 -1
- package/dist/notification-store.js +0 -1
- package/dist/pending-requests.d.ts +0 -1
- package/dist/pending-requests.js +0 -1
- package/dist/platform/index.d.ts +0 -1
- package/dist/platform/index.js +0 -1
- package/dist/platform/linux.d.ts +0 -1
- package/dist/platform/linux.js +0 -1
- package/dist/platform/macos.d.ts +0 -1
- package/dist/platform/macos.js +0 -1
- package/dist/platform/platform.d.ts +0 -1
- package/dist/platform/platform.js +0 -1
- package/dist/platform/windows.d.ts +0 -1
- package/dist/platform/windows.js +0 -1
- package/dist/pwa/assets/{index-MLEFUP3r.js → index-DWvRAUiy.js} +31 -31
- package/dist/pwa/assets/{web-B1sKCc7e.js → web-C4iZbqTC.js} +1 -1
- package/dist/pwa/assets/{web-ETD-8ZHd.js → web-CBFqJGX6.js} +1 -1
- package/dist/pwa/assets/{web-B4xEa6WO.js → web-DL4uXOpS.js} +1 -1
- package/dist/pwa/index.html +2 -2
- package/dist/rpc-handler.d.ts +0 -1
- package/dist/rpc-handler.js +0 -1
- package/dist/sms-store.d.ts +0 -1
- package/dist/sms-store.js +0 -1
- package/dist/spawn-command.d.ts +0 -1
- package/dist/spawn-command.js +0 -1
- package/dist/task.d.ts +0 -1
- package/dist/task.js +0 -1
- package/dist/transports/http-transport.d.ts +0 -1
- package/dist/transports/http-transport.js +0 -1
- package/dist/transports/nats-transport.d.ts +0 -1
- package/dist/transports/nats-transport.js +0 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -1
- package/dist/update-checker.d.ts +0 -1
- package/dist/update-checker.js +0 -1
- package/package.json +11 -1
- package/.github/workflows/ci.yml +0 -16
- package/.github/workflows/publish.yml +0 -37
- package/CLAUDE.md +0 -22
- package/dist/pwa/apple-touch-icon.png +0 -0
- package/dist/pwa/manifest.webmanifest +0 -1
- package/dist/pwa/pwa-192x192.png +0 -0
- package/dist/pwa/pwa-512x512.png +0 -0
- package/dist/pwa/registerSW.js +0 -1
- package/dist/pwa/service-worker.js +0 -2
- package/palmier-server/.github/workflows/ci.yml +0 -21
- package/palmier-server/.github/workflows/deploy.yml +0 -38
- package/palmier-server/CLAUDE.md +0 -17
- package/palmier-server/PRODUCTION.md +0 -358
- package/palmier-server/README.md +0 -231
- package/palmier-server/nats.conf +0 -19
- package/palmier-server/package.json +0 -15
- package/palmier-server/pnpm-lock.yaml +0 -7639
- package/palmier-server/pnpm-workspace.yaml +0 -3
- package/palmier-server/pwa/index.html +0 -16
- package/palmier-server/pwa/logo/logo_20260421.png +0 -0
- package/palmier-server/pwa/package.json +0 -34
- package/palmier-server/pwa/public/apple-touch-icon.png +0 -0
- package/palmier-server/pwa/public/favicon.ico +0 -0
- package/palmier-server/pwa/public/pwa-192x192.png +0 -0
- package/palmier-server/pwa/public/pwa-512x512.png +0 -0
- package/palmier-server/pwa/src/App.css +0 -3012
- package/palmier-server/pwa/src/App.tsx +0 -59
- package/palmier-server/pwa/src/agentLabels.ts +0 -11
- package/palmier-server/pwa/src/api.ts +0 -67
- package/palmier-server/pwa/src/components/CapabilityToggles.tsx +0 -170
- package/palmier-server/pwa/src/components/ConnectionStatusIcon.tsx +0 -113
- package/palmier-server/pwa/src/components/HostMenu.tsx +0 -429
- package/palmier-server/pwa/src/components/PermissionsDialog.tsx +0 -34
- package/palmier-server/pwa/src/components/PullToRefreshIndicator.tsx +0 -46
- package/palmier-server/pwa/src/components/RunDetailView.tsx +0 -343
- package/palmier-server/pwa/src/components/SessionComposer.tsx +0 -157
- package/palmier-server/pwa/src/components/SessionsView.tsx +0 -326
- package/palmier-server/pwa/src/components/SwipeToDeleteRow.tsx +0 -170
- package/palmier-server/pwa/src/components/TabBar.tsx +0 -40
- package/palmier-server/pwa/src/components/TaskCard.tsx +0 -255
- package/palmier-server/pwa/src/components/TaskForm.tsx +0 -766
- package/palmier-server/pwa/src/components/TasksView.tsx +0 -179
- package/palmier-server/pwa/src/constants.ts +0 -2
- package/palmier-server/pwa/src/contexts/HostConnectionContext.tsx +0 -432
- package/palmier-server/pwa/src/contexts/HostStoreContext.tsx +0 -124
- package/palmier-server/pwa/src/draftGuard.ts +0 -24
- package/palmier-server/pwa/src/formatTime.ts +0 -44
- package/palmier-server/pwa/src/hooks/useBackClose.ts +0 -75
- package/palmier-server/pwa/src/hooks/useMediaQuery.ts +0 -17
- package/palmier-server/pwa/src/hooks/usePullToRefresh.ts +0 -102
- package/palmier-server/pwa/src/hooks/usePushSubscription.ts +0 -77
- package/palmier-server/pwa/src/main.tsx +0 -14
- package/palmier-server/pwa/src/native/Device.ts +0 -49
- package/palmier-server/pwa/src/pages/Dashboard.tsx +0 -542
- package/palmier-server/pwa/src/pages/PairHost.tsx +0 -232
- package/palmier-server/pwa/src/pages/PairSetup.tsx +0 -134
- package/palmier-server/pwa/src/service-worker.ts +0 -142
- package/palmier-server/pwa/src/types.ts +0 -75
- package/palmier-server/pwa/src/vite-env.d.ts +0 -11
- package/palmier-server/pwa/tsconfig.json +0 -21
- package/palmier-server/pwa/tsconfig.node.json +0 -19
- package/palmier-server/pwa/vite.config.ts +0 -47
- package/palmier-server/server/.env.example +0 -20
- package/palmier-server/server/package.json +0 -36
- package/palmier-server/server/src/db.ts +0 -44
- package/palmier-server/server/src/fcm.ts +0 -74
- package/palmier-server/server/src/index.ts +0 -688
- package/palmier-server/server/src/nats-jwt.ts +0 -299
- package/palmier-server/server/src/nats-setup.ts +0 -48
- package/palmier-server/server/src/nats.ts +0 -33
- package/palmier-server/server/src/notify.ts +0 -34
- package/palmier-server/server/src/push.ts +0 -68
- package/palmier-server/server/src/routes/device.ts +0 -224
- package/palmier-server/server/src/routes/fcm.ts +0 -64
- package/palmier-server/server/src/routes/hosts.ts +0 -56
- package/palmier-server/server/src/routes/push.ts +0 -101
- package/palmier-server/server/tsconfig.json +0 -20
- package/palmier-server/spec.md +0 -533
- package/src/agents/agent-instructions.md +0 -28
- package/src/agents/agent.ts +0 -114
- package/src/agents/aider.ts +0 -35
- package/src/agents/claude.ts +0 -39
- package/src/agents/cline.ts +0 -35
- package/src/agents/codex.ts +0 -40
- package/src/agents/copilot.ts +0 -37
- package/src/agents/cursor.ts +0 -36
- package/src/agents/deepagents.ts +0 -36
- package/src/agents/droid.ts +0 -35
- package/src/agents/gemini.ts +0 -43
- package/src/agents/goose.ts +0 -33
- package/src/agents/hermes.ts +0 -36
- package/src/agents/kimi.ts +0 -35
- package/src/agents/kiro.ts +0 -36
- package/src/agents/openclaw.ts +0 -29
- package/src/agents/opencode.ts +0 -36
- package/src/agents/qoder.ts +0 -36
- package/src/agents/qwen.ts +0 -32
- package/src/agents/shared-prompt.ts +0 -30
- package/src/client-store.ts +0 -68
- package/src/commands/clients.ts +0 -29
- package/src/commands/info.ts +0 -29
- package/src/commands/init.ts +0 -165
- package/src/commands/pair.ts +0 -137
- package/src/commands/restart.ts +0 -6
- package/src/commands/run.ts +0 -608
- package/src/commands/serve.ts +0 -211
- package/src/commands/uninstall.ts +0 -9
- package/src/config.ts +0 -36
- package/src/cross-spawn.d.ts +0 -5
- package/src/event-queues.ts +0 -41
- package/src/events.ts +0 -29
- package/src/index.ts +0 -111
- package/src/linked-device.ts +0 -52
- package/src/mcp-handler.ts +0 -200
- package/src/mcp-tools.ts +0 -839
- package/src/nats-client.ts +0 -19
- package/src/network.ts +0 -96
- package/src/notification-store.ts +0 -30
- package/src/pending-requests.ts +0 -73
- package/src/platform/index.ts +0 -20
- package/src/platform/linux.ts +0 -296
- package/src/platform/macos.ts +0 -329
- package/src/platform/platform.ts +0 -31
- package/src/platform/windows.ts +0 -299
- package/src/rpc-handler.ts +0 -691
- package/src/sms-store.ts +0 -28
- package/src/spawn-command.ts +0 -123
- package/src/task.ts +0 -343
- package/src/transports/http-transport.ts +0 -478
- package/src/transports/nats-transport.ts +0 -76
- package/src/types.ts +0 -89
- package/src/update-checker.ts +0 -40
- package/test/agent-instructions.test.ts +0 -209
- package/test/agent-output-parsing.test.ts +0 -74
- package/test/linux-cron.test.ts +0 -41
- package/test/macos-plist.test.ts +0 -112
- package/test/notification-store.test.ts +0 -57
- package/test/pairing.test.ts +0 -35
- package/test/result-state.test.ts +0 -110
- package/test/task-parsing.test.ts +0 -82
- package/test/taskrun-messages.test.ts +0 -224
- package/test/tsconfig.json +0 -9
- package/test/windows-xml.test.ts +0 -89
- package/tsconfig.json +0 -19
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import { describe, it } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import * as fs from "fs";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
|
-
import { generateEndpointDocs, type ToolDefinition, type ResourceDefinition } from "../src/mcp-tools.js";
|
|
7
|
-
|
|
8
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const templatePath = path.join(__dirname, "..", "src", "agents", "agent-instructions.md");
|
|
10
|
-
const template = fs.readFileSync(templatePath, "utf-8");
|
|
11
|
-
|
|
12
|
-
/** Mock tools with a known, stable shape for testing */
|
|
13
|
-
const mockTools: ToolDefinition[] = [
|
|
14
|
-
{
|
|
15
|
-
name: "mock-action",
|
|
16
|
-
description: [
|
|
17
|
-
"Perform a mock action.",
|
|
18
|
-
'Response: `{"ok": true}` on success.',
|
|
19
|
-
],
|
|
20
|
-
inputSchema: {
|
|
21
|
-
type: "object",
|
|
22
|
-
properties: {
|
|
23
|
-
title: { type: "string", description: "Action title" },
|
|
24
|
-
detail: { type: "string", description: "Optional detail" },
|
|
25
|
-
},
|
|
26
|
-
required: ["title"],
|
|
27
|
-
},
|
|
28
|
-
handler: async () => ({ ok: true }),
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
name: "mock-query",
|
|
32
|
-
description: [
|
|
33
|
-
"Query mock data from the device.",
|
|
34
|
-
"Blocks until the device responds.",
|
|
35
|
-
'Response: `{"data": ...}` on success.',
|
|
36
|
-
],
|
|
37
|
-
inputSchema: {
|
|
38
|
-
type: "object",
|
|
39
|
-
properties: {
|
|
40
|
-
tags: {
|
|
41
|
-
type: "array",
|
|
42
|
-
items: { type: "string" },
|
|
43
|
-
description: "Filter tags",
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
handler: async () => ({ data: [] }),
|
|
48
|
-
},
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
/** Mock resources with a known, stable shape for testing */
|
|
52
|
-
const mockResources: ResourceDefinition[] = [
|
|
53
|
-
{
|
|
54
|
-
uri: "mock://data",
|
|
55
|
-
name: "Mock Data",
|
|
56
|
-
description: [
|
|
57
|
-
"Get mock data from the device.",
|
|
58
|
-
"Response: JSON array of data objects.",
|
|
59
|
-
],
|
|
60
|
-
mimeType: "application/json",
|
|
61
|
-
restPath: "/mock-data",
|
|
62
|
-
read: () => [],
|
|
63
|
-
},
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
/** Minimal replica of getAgentInstructions that doesn't need host.json */
|
|
67
|
-
function buildInstructions(taskId: string, opts?: { skipPermissions?: boolean }): string {
|
|
68
|
-
let instructions = template
|
|
69
|
-
.replace(/\{\{ENDPOINT_DOCS\}\}/g, generateEndpointDocs(7256, taskId, mockTools, mockResources))
|
|
70
|
-
.replace(/\{\{TASK_DESCRIPTION\}\}/g, "Test task prompt");
|
|
71
|
-
if (opts?.skipPermissions) {
|
|
72
|
-
instructions = instructions.replace(/## Permissions\r?\n[\s\S]*?(?=## |\r?\n---)/m, "");
|
|
73
|
-
}
|
|
74
|
-
return instructions;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
describe("getAgentInstructions", () => {
|
|
78
|
-
it("includes Permissions section by default", () => {
|
|
79
|
-
const result = buildInstructions("test-task-id");
|
|
80
|
-
assert.match(result, /## Permissions/);
|
|
81
|
-
assert.match(result, /PALMIER_PERMISSION/);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("strips Permissions section when skipPermissions is true", () => {
|
|
85
|
-
const result = buildInstructions("test-task-id", { skipPermissions: true });
|
|
86
|
-
assert.doesNotMatch(result, /## Permissions/);
|
|
87
|
-
assert.doesNotMatch(result, /PALMIER_PERMISSION/);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("preserves other sections when Permissions is stripped", () => {
|
|
91
|
-
const result = buildInstructions("test-task-id", { skipPermissions: true });
|
|
92
|
-
assert.match(result, /## Reporting Output/);
|
|
93
|
-
assert.match(result, /## Completion/);
|
|
94
|
-
assert.match(result, /## HTTP Endpoints/);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("replaces all template variables", () => {
|
|
98
|
-
const result = buildInstructions("my-task-123");
|
|
99
|
-
assert.doesNotMatch(result, /\{\{ENDPOINT_DOCS\}\}/);
|
|
100
|
-
assert.doesNotMatch(result, /\{\{TASK_DESCRIPTION\}\}/);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("includes task ID in endpoint examples", () => {
|
|
104
|
-
const result = buildInstructions("my-task-123");
|
|
105
|
-
assert.match(result, /my-task-123/);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("includes port in endpoint URL", () => {
|
|
109
|
-
const result = buildInstructions("test");
|
|
110
|
-
assert.match(result, /localhost:7256/);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("includes task description", () => {
|
|
114
|
-
const result = buildInstructions("test");
|
|
115
|
-
assert.match(result, /Test task prompt/);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
describe("generateEndpointDocs", () => {
|
|
122
|
-
const docs = generateEndpointDocs(7256, "test-id", mockTools, mockResources);
|
|
123
|
-
|
|
124
|
-
it("matches expected full output", () => {
|
|
125
|
-
const expected = [
|
|
126
|
-
"The following HTTP endpoints are available during task execution. Use curl to call them.",
|
|
127
|
-
"",
|
|
128
|
-
"**`POST http://localhost:7256/mock-action?taskId=test-id`** — Perform a mock action.",
|
|
129
|
-
"```json",
|
|
130
|
-
'{"title":"...","detail":"..."}',
|
|
131
|
-
"```",
|
|
132
|
-
"- `title` (required, string): Action title",
|
|
133
|
-
"- `detail` (optional, string): Optional detail",
|
|
134
|
-
'- Response: `{"ok": true}` on success.',
|
|
135
|
-
"",
|
|
136
|
-
"**`POST http://localhost:7256/mock-query?taskId=test-id`** — Query mock data from the device.",
|
|
137
|
-
"```json",
|
|
138
|
-
'{"tags":["..."]}',
|
|
139
|
-
"```",
|
|
140
|
-
"- `tags` (optional, string array): Filter tags",
|
|
141
|
-
"- Blocks until the device responds.",
|
|
142
|
-
'- Response: `{"data": ...}` on success.',
|
|
143
|
-
"",
|
|
144
|
-
"**`GET http://localhost:7256/mock-data?taskId=test-id`** — Get mock data from the device.",
|
|
145
|
-
"- Response: JSON array of data objects.",
|
|
146
|
-
].join("\n");
|
|
147
|
-
assert.equal(docs, expected);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("generates docs for all provided tools", () => {
|
|
151
|
-
for (const tool of mockTools) {
|
|
152
|
-
assert.match(docs, new RegExp(`POST http://localhost:7256/${tool.name}\\?taskId=`), `Missing endpoint for ${tool.name}`);
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it("includes taskId parameter for every endpoint", () => {
|
|
157
|
-
const endpointBlocks = docs.split("**`POST");
|
|
158
|
-
// First element is the header, skip it
|
|
159
|
-
for (let i = 1; i < endpointBlocks.length; i++) {
|
|
160
|
-
assert.match(endpointBlocks[i], /taskId/, `Missing taskId in endpoint block ${i}`);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it("includes port in the header", () => {
|
|
165
|
-
assert.match(docs, /localhost:7256/);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it("includes task ID in query parameters", () => {
|
|
169
|
-
assert.match(docs, /taskId=test-id/);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it("includes response descriptions", () => {
|
|
173
|
-
for (const tool of mockTools) {
|
|
174
|
-
if (tool.description.length > 1) {
|
|
175
|
-
assert.match(docs, /Response:/, `Missing response description for ${tool.name}`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it("marks required and optional parameters correctly", () => {
|
|
181
|
-
assert.match(docs, /\(required, string\)/);
|
|
182
|
-
// "detail" has no required entry, so it should be optional
|
|
183
|
-
assert.match(docs, /\(optional, string\)/);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it("handles array-type parameters", () => {
|
|
187
|
-
assert.match(docs, /\(optional, string array\)/);
|
|
188
|
-
assert.match(docs, /Filter tags/);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it("renders multi-line descriptions as bullet points", () => {
|
|
192
|
-
assert.match(docs, /- Blocks until the device responds\./);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("generates GET endpoints for all provided resources", () => {
|
|
196
|
-
for (const resource of mockResources) {
|
|
197
|
-
assert.match(docs, new RegExp(`GET http://localhost:7256${resource.restPath}`), `Missing endpoint for ${resource.uri}`);
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it("includes resource description as bullet points", () => {
|
|
202
|
-
assert.match(docs, /- Response: JSON array of data objects\./);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it("generates no resource endpoints when resources array is empty", () => {
|
|
206
|
-
const docsNoResources = generateEndpointDocs(7256, "test-id", mockTools, []);
|
|
207
|
-
assert.doesNotMatch(docsNoResources, /GET http/);
|
|
208
|
-
});
|
|
209
|
-
});
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { describe, it } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { parseTaskOutcome, parseReportFiles, parsePermissions } from "../src/commands/run.js";
|
|
4
|
-
|
|
5
|
-
describe("parseTaskOutcome", () => {
|
|
6
|
-
it("returns 'finished' for success marker", () => {
|
|
7
|
-
assert.equal(parseTaskOutcome("some output\n[PALMIER_TASK_SUCCESS]"), "finished");
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("returns 'failed' for failure marker", () => {
|
|
11
|
-
assert.equal(parseTaskOutcome("some output\n[PALMIER_TASK_FAILURE]"), "failed");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("returns 'finished' when no marker is present", () => {
|
|
15
|
-
assert.equal(parseTaskOutcome("just some regular output"), "finished");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("returns 'failed' when both markers present (failure takes priority)", () => {
|
|
19
|
-
assert.equal(parseTaskOutcome("[PALMIER_TASK_SUCCESS]\n[PALMIER_TASK_FAILURE]"), "failed");
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("only looks at last 500 chars", () => {
|
|
23
|
-
const padding = "x".repeat(600);
|
|
24
|
-
assert.equal(parseTaskOutcome("[PALMIER_TASK_FAILURE]" + padding), "finished");
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe("parseReportFiles", () => {
|
|
29
|
-
it("extracts report file names", () => {
|
|
30
|
-
const output = "doing work\n[PALMIER_REPORT] report.md\nmore work\n[PALMIER_REPORT] summary.md";
|
|
31
|
-
assert.deepEqual(parseReportFiles(output), ["report.md", "summary.md"]);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("returns empty array when no reports", () => {
|
|
35
|
-
assert.deepEqual(parseReportFiles("no reports here"), []);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("trims whitespace from file names", () => {
|
|
39
|
-
assert.deepEqual(parseReportFiles("[PALMIER_REPORT] report.md "), ["report.md"]);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("ignores placeholder examples from echoed prompt", () => {
|
|
43
|
-
const output = "[PALMIER_REPORT] <filename>\n[PALMIER_REPORT] actual-report.md";
|
|
44
|
-
assert.deepEqual(parseReportFiles(output), ["actual-report.md"]);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe("parsePermissions", () => {
|
|
49
|
-
it("extracts permissions with name and description", () => {
|
|
50
|
-
const output = "[PALMIER_PERMISSION] Read | Read file contents\n[PALMIER_PERMISSION] Bash(npm test) | Run tests";
|
|
51
|
-
const perms = parsePermissions(output);
|
|
52
|
-
assert.equal(perms.length, 2);
|
|
53
|
-
assert.deepEqual(perms[0], { name: "Read", description: "Read file contents" });
|
|
54
|
-
assert.deepEqual(perms[1], { name: "Bash(npm test)", description: "Run tests" });
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("handles permission without description", () => {
|
|
58
|
-
const perms = parsePermissions("[PALMIER_PERMISSION] Write");
|
|
59
|
-
assert.deepEqual(perms, [{ name: "Write", description: "" }]);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("returns empty array when no permissions", () => {
|
|
63
|
-
assert.deepEqual(parsePermissions("no permissions"), []);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("ignores placeholder examples from echoed prompt", () => {
|
|
67
|
-
const output = "[PALMIER_PERMISSION] <tool_name> | <description>\n[PALMIER_PERMISSION] Read | Read files";
|
|
68
|
-
const perms = parsePermissions(output);
|
|
69
|
-
assert.equal(perms.length, 1);
|
|
70
|
-
assert.deepEqual(perms[0], { name: "Read", description: "Read files" });
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
|
package/test/linux-cron.test.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { describe, it } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { cronToOnCalendar } from "../src/platform/linux.js";
|
|
4
|
-
|
|
5
|
-
describe("cronToOnCalendar", () => {
|
|
6
|
-
it("converts hourly cron", () => {
|
|
7
|
-
assert.equal(cronToOnCalendar("0 * * * *"), "*-*-* *:00:00");
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("converts daily cron", () => {
|
|
11
|
-
assert.equal(cronToOnCalendar("30 9 * * *"), "*-*-* 09:30:00");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("converts weekly Monday cron", () => {
|
|
15
|
-
assert.equal(cronToOnCalendar("0 10 * * 1"), "Mon *-*-* 10:00:00");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("converts weekly Sunday (day 0)", () => {
|
|
19
|
-
assert.equal(cronToOnCalendar("0 8 * * 0"), "Sun *-*-* 08:00:00");
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("converts weekly Sunday (day 7)", () => {
|
|
23
|
-
assert.equal(cronToOnCalendar("0 8 * * 7"), "Sun *-*-* 08:00:00");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("converts monthly cron", () => {
|
|
27
|
-
assert.equal(cronToOnCalendar("0 14 15 * *"), "*-*-15 14:00:00");
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("pads single-digit hours and minutes", () => {
|
|
31
|
-
assert.equal(cronToOnCalendar("5 3 * * *"), "*-*-* 03:05:00");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("throws on invalid cron expression", () => {
|
|
35
|
-
assert.throws(() => cronToOnCalendar("bad"), /Invalid cron/);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("throws on too few fields", () => {
|
|
39
|
-
assert.throws(() => cronToOnCalendar("0 * *"), /Invalid cron/);
|
|
40
|
-
});
|
|
41
|
-
});
|
package/test/macos-plist.test.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { describe, it } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import {
|
|
4
|
-
cronToCalendarInterval,
|
|
5
|
-
specificTimeToCalendarInterval,
|
|
6
|
-
buildPlist,
|
|
7
|
-
} from "../src/platform/macos.js";
|
|
8
|
-
|
|
9
|
-
describe("cronToCalendarInterval", () => {
|
|
10
|
-
it("converts hourly cron", () => {
|
|
11
|
-
assert.deepEqual(cronToCalendarInterval("0 * * * *"), { Minute: 0 });
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("converts daily cron", () => {
|
|
15
|
-
assert.deepEqual(cronToCalendarInterval("30 9 * * *"), { Minute: 30, Hour: 9 });
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("converts weekly Monday cron", () => {
|
|
19
|
-
assert.deepEqual(cronToCalendarInterval("0 10 * * 1"), { Minute: 0, Hour: 10, Weekday: 1 });
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("converts weekly Sunday (day 0)", () => {
|
|
23
|
-
assert.deepEqual(cronToCalendarInterval("0 8 * * 0"), { Minute: 0, Hour: 8, Weekday: 0 });
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("converts weekly Sunday (day 7 -> 0)", () => {
|
|
27
|
-
assert.deepEqual(cronToCalendarInterval("0 8 * * 7"), { Minute: 0, Hour: 8, Weekday: 0 });
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("converts monthly cron", () => {
|
|
31
|
-
assert.deepEqual(cronToCalendarInterval("0 14 15 * *"), { Minute: 0, Hour: 14, Day: 15 });
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("throws on invalid cron expression", () => {
|
|
35
|
-
assert.throws(() => cronToCalendarInterval("bad"), /Invalid cron/);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("throws on too few fields", () => {
|
|
39
|
-
assert.throws(() => cronToCalendarInterval("0 * *"), /Invalid cron/);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe("specificTimeToCalendarInterval", () => {
|
|
44
|
-
it("parses an ISO local datetime", () => {
|
|
45
|
-
assert.deepEqual(
|
|
46
|
-
specificTimeToCalendarInterval("2026-04-20T09:00"),
|
|
47
|
-
{ Month: 4, Day: 20, Hour: 9, Minute: 0 },
|
|
48
|
-
);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("parses an ISO datetime with seconds", () => {
|
|
52
|
-
assert.deepEqual(
|
|
53
|
-
specificTimeToCalendarInterval("2026-12-31T23:59:30"),
|
|
54
|
-
{ Month: 12, Day: 31, Hour: 23, Minute: 59 },
|
|
55
|
-
);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("throws on malformed input", () => {
|
|
59
|
-
assert.throws(() => specificTimeToCalendarInterval("not-a-date"), /Invalid specific_times/);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe("buildPlist", () => {
|
|
64
|
-
it("emits a valid plist envelope with ProgramArguments", () => {
|
|
65
|
-
const xml = buildPlist({
|
|
66
|
-
Label: "me.palmier.host",
|
|
67
|
-
ProgramArguments: ["/usr/local/bin/node", "/opt/palmier/dist/index.js", "serve"],
|
|
68
|
-
RunAtLoad: true,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
assert.ok(xml.startsWith(`<?xml version="1.0" encoding="UTF-8"?>`), "xml header");
|
|
72
|
-
assert.ok(xml.includes(`<!DOCTYPE plist PUBLIC`), "doctype");
|
|
73
|
-
assert.ok(xml.includes(`<plist version="1.0">`), "plist tag");
|
|
74
|
-
assert.ok(xml.includes(`<key>Label</key>`), "label key");
|
|
75
|
-
assert.ok(xml.includes(`<string>me.palmier.host</string>`), "label value");
|
|
76
|
-
assert.ok(xml.includes(`<key>ProgramArguments</key>`), "program args key");
|
|
77
|
-
assert.ok(xml.includes(`<string>/usr/local/bin/node</string>`), "program args first");
|
|
78
|
-
assert.ok(xml.includes(`<true/>`), "boolean");
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("serializes StartCalendarInterval as an array of dicts", () => {
|
|
82
|
-
const xml = buildPlist({
|
|
83
|
-
Label: "me.palmier.task.abc",
|
|
84
|
-
StartCalendarInterval: [
|
|
85
|
-
{ Minute: 0, Hour: 9 },
|
|
86
|
-
{ Minute: 30, Hour: 14, Weekday: 1 },
|
|
87
|
-
],
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
assert.ok(xml.includes(`<key>StartCalendarInterval</key>`));
|
|
91
|
-
assert.ok(xml.includes(`<array>`));
|
|
92
|
-
assert.ok(xml.includes(`<key>Minute</key>`));
|
|
93
|
-
assert.ok(xml.includes(`<integer>0</integer>`));
|
|
94
|
-
assert.ok(xml.includes(`<integer>9</integer>`));
|
|
95
|
-
assert.ok(xml.includes(`<key>Weekday</key>`));
|
|
96
|
-
assert.ok(xml.includes(`<integer>1</integer>`));
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("nests dicts (EnvironmentVariables.PATH)", () => {
|
|
100
|
-
const xml = buildPlist({
|
|
101
|
-
EnvironmentVariables: { PATH: "/usr/local/bin:/usr/bin:/bin" },
|
|
102
|
-
});
|
|
103
|
-
assert.ok(xml.includes(`<key>EnvironmentVariables</key>`));
|
|
104
|
-
assert.ok(xml.includes(`<key>PATH</key>`));
|
|
105
|
-
assert.ok(xml.includes(`<string>/usr/local/bin:/usr/bin:/bin</string>`));
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("escapes XML special characters in strings", () => {
|
|
109
|
-
const xml = buildPlist({ Label: "a & b <c>" });
|
|
110
|
-
assert.ok(xml.includes(`<string>a & b <c></string>`));
|
|
111
|
-
});
|
|
112
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { describe, it, beforeEach } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
|
|
4
|
-
// Re-import fresh module state for each test file run
|
|
5
|
-
// Since the store is module-level state, we test the exported functions directly
|
|
6
|
-
import { addNotification, getNotifications, onNotificationsChanged, type DeviceNotification } from "../src/notification-store.js";
|
|
7
|
-
|
|
8
|
-
function makeNotification(id: string, overrides?: Partial<DeviceNotification>): DeviceNotification {
|
|
9
|
-
return {
|
|
10
|
-
id,
|
|
11
|
-
packageName: "com.example.app",
|
|
12
|
-
appName: "Example",
|
|
13
|
-
title: `Title ${id}`,
|
|
14
|
-
text: `Text ${id}`,
|
|
15
|
-
timestamp: Date.now(),
|
|
16
|
-
receivedAt: Date.now(),
|
|
17
|
-
...overrides,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
describe("notification-store", () => {
|
|
22
|
-
it("stores and retrieves notifications", () => {
|
|
23
|
-
const before = getNotifications().length;
|
|
24
|
-
addNotification(makeNotification("test-1"));
|
|
25
|
-
const after = getNotifications();
|
|
26
|
-
assert.equal(after.length, before + 1);
|
|
27
|
-
assert.equal(after[after.length - 1].id, "test-1");
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("returns a defensive copy", () => {
|
|
31
|
-
const a = getNotifications();
|
|
32
|
-
const b = getNotifications();
|
|
33
|
-
assert.notStrictEqual(a, b);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("evicts oldest when exceeding max", () => {
|
|
37
|
-
const before = getNotifications().length;
|
|
38
|
-
// Add enough to exceed 50
|
|
39
|
-
for (let i = 0; i < 60; i++) {
|
|
40
|
-
addNotification(makeNotification(`evict-${i}`));
|
|
41
|
-
}
|
|
42
|
-
const result = getNotifications();
|
|
43
|
-
assert.ok(result.length <= 50, `Expected <= 50, got ${result.length}`);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("notifies listeners on add", () => {
|
|
47
|
-
let called = 0;
|
|
48
|
-
const unsub = onNotificationsChanged(() => { called++; });
|
|
49
|
-
addNotification(makeNotification("listener-1"));
|
|
50
|
-
assert.equal(called, 1);
|
|
51
|
-
addNotification(makeNotification("listener-2"));
|
|
52
|
-
assert.equal(called, 2);
|
|
53
|
-
unsub();
|
|
54
|
-
addNotification(makeNotification("listener-3"));
|
|
55
|
-
assert.equal(called, 2); // no longer called after unsubscribe
|
|
56
|
-
});
|
|
57
|
-
});
|
package/test/pairing.test.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { describe, it } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { generatePairingCode, PAIRING_EXPIRY_MS } from "../src/commands/pair.js";
|
|
4
|
-
|
|
5
|
-
describe("generatePairingCode", () => {
|
|
6
|
-
it("generates a 6-character code", () => {
|
|
7
|
-
const code = generatePairingCode();
|
|
8
|
-
assert.equal(code.length, 6);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it("only contains allowed characters (no O/0/I/1/L)", () => {
|
|
12
|
-
const allowed = "ABCDEFGHJKMNPQRSTUVWXYZ23456789";
|
|
13
|
-
for (let i = 0; i < 50; i++) {
|
|
14
|
-
const code = generatePairingCode();
|
|
15
|
-
for (const ch of code) {
|
|
16
|
-
assert.ok(allowed.includes(ch), `Character '${ch}' is not in allowed set`);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("generates unique codes", () => {
|
|
22
|
-
const codes = new Set<string>();
|
|
23
|
-
for (let i = 0; i < 100; i++) {
|
|
24
|
-
codes.add(generatePairingCode());
|
|
25
|
-
}
|
|
26
|
-
// With 30^6 ≈ 729M possibilities, 100 codes should all be unique
|
|
27
|
-
assert.equal(codes.size, 100);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe("PAIRING_EXPIRY_MS", () => {
|
|
32
|
-
it("is 1 minute", () => {
|
|
33
|
-
assert.equal(PAIRING_EXPIRY_MS, 60 * 1000);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { describe, it, beforeEach } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import * as fs from "fs";
|
|
4
|
-
import * as os from "os";
|
|
5
|
-
import * as path from "path";
|
|
6
|
-
import {
|
|
7
|
-
createRunDir,
|
|
8
|
-
appendRunMessage,
|
|
9
|
-
beginStreamingMessage,
|
|
10
|
-
} from "../src/task.js";
|
|
11
|
-
import { parseResultFrontmatter } from "../src/rpc-handler.js";
|
|
12
|
-
|
|
13
|
-
let taskDir: string;
|
|
14
|
-
let runId: string;
|
|
15
|
-
|
|
16
|
-
function setup() {
|
|
17
|
-
taskDir = fs.mkdtempSync(path.join(os.tmpdir(), "palmier-test-"));
|
|
18
|
-
runId = createRunDir(taskDir, "Test Task", 1000, "claude");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function readRaw(): string {
|
|
22
|
-
return fs.readFileSync(path.join(taskDir, runId, "TASKRUN.md"), "utf-8");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe("parseResultFrontmatter — monitoring state", () => {
|
|
26
|
-
beforeEach(setup);
|
|
27
|
-
|
|
28
|
-
it("returns 'monitoring' when monitoring is the last message", () => {
|
|
29
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1000, content: "", type: "started" });
|
|
30
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1001, content: "", type: "monitoring" });
|
|
31
|
-
|
|
32
|
-
const result = parseResultFrontmatter(readRaw());
|
|
33
|
-
assert.equal(result.running_state, "monitoring");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("returns 'started' when an assistant message follows monitoring", () => {
|
|
37
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1000, content: "", type: "started" });
|
|
38
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1001, content: "", type: "monitoring" });
|
|
39
|
-
const writer = beginStreamingMessage(taskDir, runId, 1002);
|
|
40
|
-
writer.write("Working on it...");
|
|
41
|
-
writer.end();
|
|
42
|
-
|
|
43
|
-
const result = parseResultFrontmatter(readRaw());
|
|
44
|
-
assert.equal(result.running_state, "started");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("returns 'monitoring' after agent finishes and monitoring resumes", () => {
|
|
48
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1000, content: "", type: "started" });
|
|
49
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1001, content: "", type: "monitoring" });
|
|
50
|
-
// Agent processes a line
|
|
51
|
-
const writer = beginStreamingMessage(taskDir, runId, 1002);
|
|
52
|
-
writer.write("Done processing line.");
|
|
53
|
-
writer.end();
|
|
54
|
-
// Back to monitoring
|
|
55
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1003, content: "", type: "monitoring" });
|
|
56
|
-
|
|
57
|
-
const result = parseResultFrontmatter(readRaw());
|
|
58
|
-
assert.equal(result.running_state, "monitoring");
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("returns 'started' when a user message follows monitoring", () => {
|
|
62
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1000, content: "", type: "started" });
|
|
63
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1001, content: "", type: "monitoring" });
|
|
64
|
-
appendRunMessage(taskDir, runId, { role: "user", time: 1002, content: "some input" });
|
|
65
|
-
|
|
66
|
-
const result = parseResultFrontmatter(readRaw());
|
|
67
|
-
assert.equal(result.running_state, "started");
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
describe("parseResultFrontmatter — standard states", () => {
|
|
72
|
-
beforeEach(setup);
|
|
73
|
-
|
|
74
|
-
it("returns 'started' for a running task", () => {
|
|
75
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1000, content: "", type: "started" });
|
|
76
|
-
appendRunMessage(taskDir, runId, { role: "user", time: 1001, content: "Do something" });
|
|
77
|
-
|
|
78
|
-
const result = parseResultFrontmatter(readRaw());
|
|
79
|
-
assert.equal(result.running_state, "started");
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("returns 'finished' for a completed task", () => {
|
|
83
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1000, content: "", type: "started" });
|
|
84
|
-
appendRunMessage(taskDir, runId, { role: "user", time: 1001, content: "Do something" });
|
|
85
|
-
const writer = beginStreamingMessage(taskDir, runId, 1002);
|
|
86
|
-
writer.write("Done.");
|
|
87
|
-
writer.end();
|
|
88
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1003, content: "", type: "finished" });
|
|
89
|
-
|
|
90
|
-
const result = parseResultFrontmatter(readRaw());
|
|
91
|
-
assert.equal(result.running_state, "finished");
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("returns 'failed' for a failed task", () => {
|
|
95
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1000, content: "", type: "started" });
|
|
96
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1001, content: "", type: "failed" });
|
|
97
|
-
|
|
98
|
-
const result = parseResultFrontmatter(readRaw());
|
|
99
|
-
assert.equal(result.running_state, "failed");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("returns 'followup' when started again after terminal state", () => {
|
|
103
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1000, content: "", type: "started" });
|
|
104
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1001, content: "", type: "finished" });
|
|
105
|
-
appendRunMessage(taskDir, runId, { role: "status", time: 1002, content: "", type: "started" });
|
|
106
|
-
|
|
107
|
-
const result = parseResultFrontmatter(readRaw());
|
|
108
|
-
assert.equal(result.running_state, "followup");
|
|
109
|
-
});
|
|
110
|
-
});
|