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
package/src/agents/qoder.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { ParsedTask, RequiredPermission } from "../types.js";
|
|
2
|
-
import { execSync } from "child_process";
|
|
3
|
-
import type { AgentTool, CommandLine } from "./agent.js";
|
|
4
|
-
import { getAgentInstructions } from "./shared-prompt.js";
|
|
5
|
-
import { SHELL } from "../platform/index.js";
|
|
6
|
-
|
|
7
|
-
export class Qoder implements AgentTool {
|
|
8
|
-
supportsPermissions = false;
|
|
9
|
-
supportsYolo = true;
|
|
10
|
-
getPromptCommandLine(prompt: string): CommandLine {
|
|
11
|
-
return { command: "qodercli", args: ["-p", prompt] };
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
|
|
15
|
-
const yolo = extraPermissions === "yolo";
|
|
16
|
-
const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
|
|
17
|
-
const args = [];
|
|
18
|
-
|
|
19
|
-
if (yolo) {
|
|
20
|
-
args.push("--yolo");
|
|
21
|
-
}
|
|
22
|
-
if (followupPrompt) {args.push("-c");}
|
|
23
|
-
args.push("-p", prompt);
|
|
24
|
-
|
|
25
|
-
return { command: "qodercli", args};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async init(): Promise<boolean> {
|
|
29
|
-
try {
|
|
30
|
-
execSync("qodercli --version", { stdio: "ignore", shell: SHELL });
|
|
31
|
-
} catch {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
}
|
package/src/agents/qwen.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { ParsedTask, RequiredPermission } from "../types.js";
|
|
2
|
-
import { execSync } from "child_process";
|
|
3
|
-
import type { AgentTool, CommandLine } from "./agent.js";
|
|
4
|
-
import { getAgentInstructions } from "./shared-prompt.js";
|
|
5
|
-
import { SHELL } from "../platform/index.js";
|
|
6
|
-
|
|
7
|
-
export class QwenAgent implements AgentTool {
|
|
8
|
-
supportsPermissions = false;
|
|
9
|
-
supportsYolo = true;
|
|
10
|
-
getPromptCommandLine(prompt: string): CommandLine {
|
|
11
|
-
return { command: "qwen", args: ["-p", prompt] };
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
|
|
15
|
-
const yolo = extraPermissions === "yolo";
|
|
16
|
-
const prompt = followupPrompt ?? getAgentInstructions(task, yolo || !this.supportsPermissions);
|
|
17
|
-
const args = ["--approval-mode", yolo ? "yolo" : "auto-edit"];
|
|
18
|
-
|
|
19
|
-
if (followupPrompt) { args.push("-c"); }
|
|
20
|
-
args.push("-p", prompt);
|
|
21
|
-
return { command: "qwen", args };
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async init(): Promise<boolean> {
|
|
25
|
-
try {
|
|
26
|
-
execSync("qwen --version", { stdio: "ignore", shell: SHELL });
|
|
27
|
-
} catch {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import { fileURLToPath } from "url";
|
|
4
|
-
import { loadConfig } from "../config.js";
|
|
5
|
-
import { generateEndpointDocs } from "../mcp-tools.js";
|
|
6
|
-
import type { ParsedTask } from "../types.js";
|
|
7
|
-
|
|
8
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
|
|
10
|
-
const AGENT_INSTRUCTIONS_TEMPLATE = fs.readFileSync(
|
|
11
|
-
path.join(__dirname, "agent-instructions.md"),
|
|
12
|
-
"utf-8",
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
export function getAgentInstructions(task: ParsedTask, skipPermissions?: boolean): string {
|
|
16
|
-
const port = loadConfig().httpPort ?? 7256;
|
|
17
|
-
const taskDescription = task.frontmatter.user_prompt;
|
|
18
|
-
let instructions = AGENT_INSTRUCTIONS_TEMPLATE
|
|
19
|
-
.replace(/\{\{ENDPOINT_DOCS\}\}/g, generateEndpointDocs(port, task.frontmatter.id))
|
|
20
|
-
.replace(/\{\{TASK_DESCRIPTION\}\}/g, taskDescription);
|
|
21
|
-
if (skipPermissions) {
|
|
22
|
-
instructions = instructions.replace(/## Permissions\r?\n[\s\S]*?(?=## |\r?\n---)/m, "");
|
|
23
|
-
}
|
|
24
|
-
return instructions;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const TASK_SUCCESS_MARKER = "[PALMIER_TASK_SUCCESS]";
|
|
28
|
-
export const TASK_FAILURE_MARKER = "[PALMIER_TASK_FAILURE]";
|
|
29
|
-
export const TASK_REPORT_PREFIX = "[PALMIER_REPORT]";
|
|
30
|
-
export const TASK_PERMISSION_PREFIX = "[PALMIER_PERMISSION]";
|
package/src/client-store.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import { randomBytes } from "crypto";
|
|
4
|
-
import { CONFIG_DIR } from "./config.js";
|
|
5
|
-
|
|
6
|
-
const CLIENTS_FILE = path.join(CONFIG_DIR, "clients.json");
|
|
7
|
-
|
|
8
|
-
export interface ClientEntry {
|
|
9
|
-
token: string;
|
|
10
|
-
createdAt: string;
|
|
11
|
-
label?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function readFile(): ClientEntry[] {
|
|
15
|
-
try {
|
|
16
|
-
if (!fs.existsSync(CLIENTS_FILE)) return [];
|
|
17
|
-
const raw = fs.readFileSync(CLIENTS_FILE, "utf-8");
|
|
18
|
-
return JSON.parse(raw) as ClientEntry[];
|
|
19
|
-
} catch {
|
|
20
|
-
return [];
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function writeFile(clients: ClientEntry[]): void {
|
|
25
|
-
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
26
|
-
fs.writeFileSync(CLIENTS_FILE, JSON.stringify(clients, null, 2), "utf-8");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function loadClients(): ClientEntry[] {
|
|
30
|
-
return readFile();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function addClient(label?: string): ClientEntry {
|
|
34
|
-
const clients = readFile();
|
|
35
|
-
const entry: ClientEntry = {
|
|
36
|
-
token: randomBytes(32).toString("hex"),
|
|
37
|
-
createdAt: new Date().toISOString(),
|
|
38
|
-
...(label ? { label } : {}),
|
|
39
|
-
};
|
|
40
|
-
clients.push(entry);
|
|
41
|
-
writeFile(clients);
|
|
42
|
-
return entry;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function revokeClient(token: string): boolean {
|
|
46
|
-
const clients = readFile();
|
|
47
|
-
const idx = clients.findIndex((c) => c.token === token);
|
|
48
|
-
if (idx === -1) return false;
|
|
49
|
-
clients.splice(idx, 1);
|
|
50
|
-
writeFile(clients);
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function revokeAllClients(): number {
|
|
55
|
-
const clients = readFile();
|
|
56
|
-
const count = clients.length;
|
|
57
|
-
writeFile([]);
|
|
58
|
-
return count;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function validateClient(token: string): boolean {
|
|
62
|
-
const clients = readFile();
|
|
63
|
-
return clients.some((c) => c.token === token);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function hasClients(): boolean {
|
|
67
|
-
return readFile().length > 0;
|
|
68
|
-
}
|
package/src/commands/clients.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { loadClients, revokeClient, revokeAllClients } from "../client-store.js";
|
|
2
|
-
|
|
3
|
-
export async function clientsListCommand(): Promise<void> {
|
|
4
|
-
const clients = loadClients();
|
|
5
|
-
if (clients.length === 0) {
|
|
6
|
-
console.log("No active clients.");
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
console.log(`${clients.length} active client(s):\n`);
|
|
11
|
-
for (const c of clients) {
|
|
12
|
-
const label = c.label ? ` (${c.label})` : "";
|
|
13
|
-
console.log(` ${c.token}${label} created ${c.createdAt}`);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export async function clientsRevokeCommand(token: string): Promise<void> {
|
|
18
|
-
if (revokeClient(token)) {
|
|
19
|
-
console.log("Client revoked.");
|
|
20
|
-
} else {
|
|
21
|
-
console.error("Client not found.");
|
|
22
|
-
process.exit(1);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function clientsRevokeAllCommand(): Promise<void> {
|
|
27
|
-
const count = revokeAllClients();
|
|
28
|
-
console.log(`Revoked ${count} client(s).`);
|
|
29
|
-
}
|
package/src/commands/info.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { loadConfig } from "../config.js";
|
|
2
|
-
import { loadClients } from "../client-store.js";
|
|
3
|
-
import { buildLanUrl } from "../network.js";
|
|
4
|
-
|
|
5
|
-
export async function infoCommand(): Promise<void> {
|
|
6
|
-
const config = loadConfig();
|
|
7
|
-
const clients = loadClients();
|
|
8
|
-
const port = config.httpPort ?? 7256;
|
|
9
|
-
|
|
10
|
-
const lanUrl = buildLanUrl(port, config.defaultInterface);
|
|
11
|
-
|
|
12
|
-
console.log(`Host ID: ${config.hostId}`);
|
|
13
|
-
console.log(`Project root: ${config.projectRoot}`);
|
|
14
|
-
console.log(`Local URL: http://localhost:${port}`);
|
|
15
|
-
console.log(`LAN URL: ${lanUrl ?? "(default route interface unavailable — pair a device or check network)"}`);
|
|
16
|
-
|
|
17
|
-
if (config.agents && config.agents.length > 0) {
|
|
18
|
-
console.log(`Agents: ${config.agents.map((a) => a.label).join(", ")}`);
|
|
19
|
-
} else {
|
|
20
|
-
console.log(`Agents: (none detected — run \`palmier agents\`)`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
console.log(`Clients: ${clients.length} active`);
|
|
24
|
-
|
|
25
|
-
if (clients.length === 0) {
|
|
26
|
-
console.log("");
|
|
27
|
-
console.log("No paired clients. Run `palmier pair` to connect a device.");
|
|
28
|
-
}
|
|
29
|
-
}
|
package/src/commands/init.ts
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import * as readline from "readline";
|
|
2
|
-
import { loadConfig, saveConfig } from "../config.js";
|
|
3
|
-
import { detectAgents } from "../agents/agent.js";
|
|
4
|
-
import { getPlatform } from "../platform/index.js";
|
|
5
|
-
import { pairCommand } from "./pair.js";
|
|
6
|
-
import { detectDefaultInterface, getInterfaceIpv4 } from "../network.js";
|
|
7
|
-
import { listTasks } from "../task.js";
|
|
8
|
-
import type { HostConfig } from "../types.js";
|
|
9
|
-
|
|
10
|
-
type AskFn = (q: string) => Promise<string>;
|
|
11
|
-
|
|
12
|
-
const bold = (s: string) => `\x1b[1m${s}\x1b[0m`;
|
|
13
|
-
const dim = (s: string) => `\x1b[2m${s}\x1b[0m`;
|
|
14
|
-
const green = (s: string) => `\x1b[32m${s}\x1b[0m`;
|
|
15
|
-
const cyan = (s: string) => `\x1b[36m${s}\x1b[0m`;
|
|
16
|
-
const red = (s: string) => `\x1b[31m${s}\x1b[0m`;
|
|
17
|
-
|
|
18
|
-
export async function initCommand(): Promise<void> {
|
|
19
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
20
|
-
const ask: AskFn = (q) => new Promise<string>((resolve) => rl.question(q, resolve));
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
console.log(`\n${bold("=== Palmier Host Setup ===")}\n`);
|
|
24
|
-
console.log(`By continuing, you agree to the ${cyan("Terms of Service")} (https://www.palmier.me/terms)`);
|
|
25
|
-
console.log(`and ${cyan("Privacy Policy")} (https://www.palmier.me/privacy).\n`);
|
|
26
|
-
|
|
27
|
-
console.log("Detecting installed agents...");
|
|
28
|
-
const agents = await detectAgents();
|
|
29
|
-
|
|
30
|
-
if (agents.length === 0) {
|
|
31
|
-
console.log(`\n${red("No agent CLIs detected.")} Palmier requires at least one supported agent CLI.\n`);
|
|
32
|
-
console.log(`See supported agents: https://www.palmier.me/agents\n`);
|
|
33
|
-
console.log(`Install at least one agent CLI, then run ${cyan("palmier init")} again.`);
|
|
34
|
-
rl.close();
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
console.log(` Found: ${green(agents.map((a) => a.label).join(", "))}\n`);
|
|
39
|
-
|
|
40
|
-
let httpPort = 7256;
|
|
41
|
-
const portAnswer = await ask(`HTTP port (default ${httpPort}): `);
|
|
42
|
-
const parsed = parseInt(portAnswer.trim(), 10);
|
|
43
|
-
if (parsed > 0 && parsed < 65536) httpPort = parsed;
|
|
44
|
-
|
|
45
|
-
const defaultInterface = (await detectDefaultInterface()) ?? undefined;
|
|
46
|
-
const lanIp = defaultInterface ? getInterfaceIpv4(defaultInterface) : null;
|
|
47
|
-
|
|
48
|
-
console.log(`\n${bold("Setup summary:")}\n`);
|
|
49
|
-
console.log(` ${dim("Task storage:")} ${bold(process.cwd())}`);
|
|
50
|
-
console.log(` All tasks and execution data will be stored here.\n`);
|
|
51
|
-
console.log(` ${dim("Local:")} ${cyan(`http://localhost:${httpPort}`)}`);
|
|
52
|
-
console.log(` Open in a browser on this machine — no internet required.\n`);
|
|
53
|
-
console.log(` ${dim("Remote (app):")} ${cyan("https://github.com/caihongxu/palmier-android/releases")}`);
|
|
54
|
-
if (lanIp) {
|
|
55
|
-
console.log(` Download the Android APK. The app uses LAN for direct RPC`);
|
|
56
|
-
console.log(` on the same network (detected ${cyan(`http://${lanIp}:${httpPort}`)}),`);
|
|
57
|
-
console.log(` otherwise the relay.\n`);
|
|
58
|
-
} else {
|
|
59
|
-
console.log(` Download the Android APK. Traffic will go through the relay —`);
|
|
60
|
-
console.log(` ${red("could not detect a LAN interface")} for direct RPC.\n`);
|
|
61
|
-
}
|
|
62
|
-
console.log(` ${dim("Remote (web):")} ${cyan("https://app.palmier.me")}`);
|
|
63
|
-
console.log(` Pair a browser on any device. Traffic always goes through the relay.\n`);
|
|
64
|
-
console.log(` ${dim("Agents:")} ${agents.map((a) => a.label).join(", ")}\n`);
|
|
65
|
-
|
|
66
|
-
const existingTasks = listTasks(process.cwd());
|
|
67
|
-
if (existingTasks.length > 0) {
|
|
68
|
-
console.log(` ${dim("Recover tasks:")} ${existingTasks.length} existing task(s) found:`);
|
|
69
|
-
for (const t of existingTasks) {
|
|
70
|
-
console.log(` - ${t.frontmatter.name || t.frontmatter.user_prompt.slice(0, 50)}`);
|
|
71
|
-
}
|
|
72
|
-
console.log();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const confirm = await ask("Proceed? (Y/n): ");
|
|
76
|
-
if (confirm.trim().toLowerCase() === "n") {
|
|
77
|
-
console.log("\nSetup cancelled.");
|
|
78
|
-
rl.close();
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
let existingHostId: string | undefined;
|
|
83
|
-
try { existingHostId = loadConfig().hostId; } catch { /* first init */ }
|
|
84
|
-
|
|
85
|
-
const serverUrl = "https://app.palmier.me";
|
|
86
|
-
let registerResponse: { hostId: string; natsUrl: string; natsWsUrl: string; natsJwt: string; natsNkeySeed: string };
|
|
87
|
-
|
|
88
|
-
while (true) {
|
|
89
|
-
console.log(`\nRegistering host...`);
|
|
90
|
-
try {
|
|
91
|
-
registerResponse = await registerHost(serverUrl, existingHostId);
|
|
92
|
-
console.log(green("Host registered successfully."));
|
|
93
|
-
break;
|
|
94
|
-
} catch (err) {
|
|
95
|
-
console.error(`\n ${red(err instanceof Error ? err.message : String(err))}`);
|
|
96
|
-
const retry = await ask("\nRetry? (Y/n): ");
|
|
97
|
-
if (retry.trim().toLowerCase() === "n") {
|
|
98
|
-
console.log("\nSetup cancelled.");
|
|
99
|
-
rl.close();
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const config: HostConfig = {
|
|
106
|
-
hostId: registerResponse.hostId,
|
|
107
|
-
projectRoot: process.cwd(),
|
|
108
|
-
natsUrl: registerResponse.natsUrl,
|
|
109
|
-
natsWsUrl: registerResponse.natsWsUrl,
|
|
110
|
-
natsJwt: registerResponse.natsJwt,
|
|
111
|
-
natsNkeySeed: registerResponse.natsNkeySeed,
|
|
112
|
-
agents,
|
|
113
|
-
httpPort,
|
|
114
|
-
defaultInterface,
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
saveConfig(config);
|
|
118
|
-
console.log(`Config saved to ${dim("~/.config/palmier/host.json")}`);
|
|
119
|
-
|
|
120
|
-
getPlatform().installDaemon(config);
|
|
121
|
-
|
|
122
|
-
// Task recovery runs in the daemon (palmier serve) because that process
|
|
123
|
-
// is elevated and can create S4U scheduled tasks.
|
|
124
|
-
|
|
125
|
-
console.log("\nStarting pairing...");
|
|
126
|
-
rl.close();
|
|
127
|
-
await pairCommand();
|
|
128
|
-
} catch (err) {
|
|
129
|
-
rl.close();
|
|
130
|
-
throw err;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
async function registerHost(
|
|
136
|
-
serverUrl: string,
|
|
137
|
-
existingHostId?: string,
|
|
138
|
-
): Promise<{ hostId: string; natsUrl: string; natsWsUrl: string; natsJwt: string; natsNkeySeed: string }> {
|
|
139
|
-
try {
|
|
140
|
-
const res = await fetch(`${serverUrl}/api/hosts/register`, {
|
|
141
|
-
method: "POST",
|
|
142
|
-
headers: { "Content-Type": "application/json" },
|
|
143
|
-
body: JSON.stringify(existingHostId ? { hostId: existingHostId } : {}),
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
if (!res.ok) {
|
|
147
|
-
const body = await res.text();
|
|
148
|
-
throw new Error(`${res.status} ${res.statusText}\n${body}`);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return (await res.json()) as {
|
|
152
|
-
hostId: string;
|
|
153
|
-
natsUrl: string;
|
|
154
|
-
natsWsUrl: string;
|
|
155
|
-
natsJwt: string;
|
|
156
|
-
natsNkeySeed: string;
|
|
157
|
-
};
|
|
158
|
-
} catch (err) {
|
|
159
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
160
|
-
if (message.includes("fetch failed") || message.includes("ECONNREFUSED") || message.includes("ENOTFOUND") || message.includes("NetworkError")) {
|
|
161
|
-
throw new Error(`Could not reach ${serverUrl} — check the URL and your network connection.`);
|
|
162
|
-
}
|
|
163
|
-
throw new Error(`Failed to register host: ${message}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
package/src/commands/pair.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import * as http from "node:http";
|
|
2
|
-
import * as os from "node:os";
|
|
3
|
-
import { StringCodec } from "nats";
|
|
4
|
-
import { loadConfig } from "../config.js";
|
|
5
|
-
import { connectNats } from "../nats-client.js";
|
|
6
|
-
import { addClient } from "../client-store.js";
|
|
7
|
-
import type { HostConfig } from "../types.js";
|
|
8
|
-
|
|
9
|
-
const CODE_CHARS = "ABCDEFGHJKMNPQRSTUVWXYZ23456789"; // no O/0/I/1/L
|
|
10
|
-
const CODE_LENGTH = 6;
|
|
11
|
-
|
|
12
|
-
export const PAIRING_EXPIRY_MS = 60 * 1000; // 1 minute
|
|
13
|
-
|
|
14
|
-
export function generatePairingCode(): string {
|
|
15
|
-
const bytes = new Uint8Array(CODE_LENGTH);
|
|
16
|
-
crypto.getRandomValues(bytes);
|
|
17
|
-
return Array.from(bytes, (b) => CODE_CHARS[b % CODE_CHARS.length]).join("");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function buildPairResponse(config: HostConfig, label?: string) {
|
|
21
|
-
const client = addClient(label);
|
|
22
|
-
return {
|
|
23
|
-
hostId: config.hostId,
|
|
24
|
-
clientToken: client.token,
|
|
25
|
-
hostName: os.hostname(),
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function httpPairRegister(port: number, code: string): Promise<boolean> {
|
|
30
|
-
const body = JSON.stringify({ code, expiryMs: PAIRING_EXPIRY_MS });
|
|
31
|
-
|
|
32
|
-
return new Promise((resolve) => {
|
|
33
|
-
const req = http.request(
|
|
34
|
-
{
|
|
35
|
-
hostname: "127.0.0.1",
|
|
36
|
-
port,
|
|
37
|
-
path: "/pair-register",
|
|
38
|
-
method: "POST",
|
|
39
|
-
headers: { "Content-Type": "application/json" },
|
|
40
|
-
timeout: PAIRING_EXPIRY_MS + 5000,
|
|
41
|
-
},
|
|
42
|
-
(res) => {
|
|
43
|
-
const chunks: Buffer[] = [];
|
|
44
|
-
res.on("data", (chunk: Buffer) => chunks.push(chunk));
|
|
45
|
-
res.on("end", () => {
|
|
46
|
-
try {
|
|
47
|
-
const result = JSON.parse(Buffer.concat(chunks).toString("utf-8")) as { paired: boolean };
|
|
48
|
-
resolve(result.paired);
|
|
49
|
-
} catch {
|
|
50
|
-
resolve(false);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
},
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
req.on("error", () => resolve(false));
|
|
57
|
-
req.on("timeout", () => { req.destroy(); resolve(false); });
|
|
58
|
-
req.end(body);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Listens on NATS (server mode) and HTTP (via serve daemon) in parallel. */
|
|
63
|
-
export async function pairCommand(): Promise<void> {
|
|
64
|
-
const config = loadConfig();
|
|
65
|
-
const code = generatePairingCode();
|
|
66
|
-
const httpPort = config.httpPort ?? 7256;
|
|
67
|
-
|
|
68
|
-
let paired = false;
|
|
69
|
-
|
|
70
|
-
function onPaired() {
|
|
71
|
-
paired = true;
|
|
72
|
-
console.log("Paired successfully!");
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const cleanups: Array<() => void | Promise<void>> = [];
|
|
76
|
-
|
|
77
|
-
console.log("");
|
|
78
|
-
console.log("Enter this code in your Palmier app:");
|
|
79
|
-
console.log("");
|
|
80
|
-
console.log(` ${code}`);
|
|
81
|
-
console.log("");
|
|
82
|
-
console.log("Code expires in 1 minute.");
|
|
83
|
-
|
|
84
|
-
const nc = await connectNats(config);
|
|
85
|
-
const sc = StringCodec();
|
|
86
|
-
const subject = `pair.${code}`;
|
|
87
|
-
const sub = nc.subscribe(subject, { max: 1 });
|
|
88
|
-
|
|
89
|
-
cleanups.push(() => {
|
|
90
|
-
sub.unsubscribe();
|
|
91
|
-
nc.close();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
(async () => {
|
|
95
|
-
for await (const msg of sub) {
|
|
96
|
-
if (paired) break;
|
|
97
|
-
let label: string | undefined;
|
|
98
|
-
try {
|
|
99
|
-
if (msg.data && msg.data.length > 0) {
|
|
100
|
-
const body = JSON.parse(sc.decode(msg.data)) as { label?: string };
|
|
101
|
-
label = body.label;
|
|
102
|
-
}
|
|
103
|
-
} catch { /* empty body is fine */ }
|
|
104
|
-
|
|
105
|
-
const response = buildPairResponse(config, label);
|
|
106
|
-
if (msg.reply) {
|
|
107
|
-
msg.respond(sc.encode(JSON.stringify(response)));
|
|
108
|
-
}
|
|
109
|
-
onPaired();
|
|
110
|
-
}
|
|
111
|
-
})();
|
|
112
|
-
|
|
113
|
-
(async () => {
|
|
114
|
-
const result = await httpPairRegister(httpPort, code);
|
|
115
|
-
if (result) onPaired();
|
|
116
|
-
})();
|
|
117
|
-
|
|
118
|
-
const start = Date.now();
|
|
119
|
-
await new Promise<void>((resolve) => {
|
|
120
|
-
const interval = setInterval(() => {
|
|
121
|
-
if (paired || Date.now() - start >= PAIRING_EXPIRY_MS) {
|
|
122
|
-
clearInterval(interval);
|
|
123
|
-
resolve();
|
|
124
|
-
}
|
|
125
|
-
}, 500);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
for (const cleanup of cleanups) {
|
|
129
|
-
await cleanup();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (!paired) {
|
|
133
|
-
console.log("Code expired. Run `palmier pair` to try again.");
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
process.exit(paired ? 0 : 1);
|
|
137
|
-
}
|