mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 05:17:40 +00:00
Revert "iOS: wire node services and tests"
This reverts commit 7b0a0f3dac.
This commit is contained in:
@@ -133,52 +133,3 @@ describe("nodes run", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("nodes invoke", () => {
|
||||
beforeEach(() => {
|
||||
callGateway.mockReset();
|
||||
});
|
||||
|
||||
it("invokes arbitrary commands with params JSON", async () => {
|
||||
callGateway.mockImplementation(async ({ method, params }) => {
|
||||
if (method === "node.list") {
|
||||
return { nodes: [{ nodeId: "ios-1" }] };
|
||||
}
|
||||
if (method === "node.invoke") {
|
||||
expect(params).toMatchObject({
|
||||
nodeId: "ios-1",
|
||||
command: "device.info",
|
||||
params: { includeBattery: true },
|
||||
timeoutMs: 12_000,
|
||||
});
|
||||
return {
|
||||
ok: true,
|
||||
nodeId: "ios-1",
|
||||
command: "device.info",
|
||||
payload: { deviceName: "iPhone" },
|
||||
};
|
||||
}
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
});
|
||||
|
||||
const tool = createOpenClawTools().find((candidate) => candidate.name === "nodes");
|
||||
if (!tool) {
|
||||
throw new Error("missing nodes tool");
|
||||
}
|
||||
|
||||
const result = await tool.execute("call1", {
|
||||
action: "invoke",
|
||||
node: "ios-1",
|
||||
invokeCommand: "device.info",
|
||||
invokeParamsJson: JSON.stringify({ includeBattery: true }),
|
||||
invokeTimeoutMs: 12_000,
|
||||
});
|
||||
|
||||
expect(result.details).toMatchObject({
|
||||
ok: true,
|
||||
nodeId: "ios-1",
|
||||
command: "device.info",
|
||||
payload: { deviceName: "iPhone" },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -229,7 +229,7 @@ export function buildAgentSystemPrompt(params: {
|
||||
// Channel docking: add login tools here when a channel needs interactive linking.
|
||||
browser: "Control web browser",
|
||||
canvas: "Present/eval/snapshot the Canvas",
|
||||
nodes: "List/describe/notify/camera/screen/invoke on paired nodes",
|
||||
nodes: "List/describe/notify/camera/screen on paired nodes",
|
||||
cron: "Manage cron jobs and wake events (use for reminders; when scheduling a reminder, write the systemEvent text as something that will read like a reminder when it fires, and mention that it is a reminder depending on the time gap between setting and firing; include recent context in reminder text if appropriate)",
|
||||
message: "Send messages and channel actions",
|
||||
gateway: "Restart, apply config, or run updates on the running OpenClaw process",
|
||||
@@ -382,7 +382,7 @@ export function buildAgentSystemPrompt(params: {
|
||||
`- ${processToolName}: manage background exec sessions`,
|
||||
"- browser: control openclaw's dedicated browser",
|
||||
"- canvas: present/eval/snapshot the Canvas",
|
||||
"- nodes: list/describe/notify/camera/screen/invoke on paired nodes",
|
||||
"- nodes: list/describe/notify/camera/screen on paired nodes",
|
||||
"- cron: manage cron jobs and wake events (use for reminders; when scheduling a reminder, write the systemEvent text as something that will read like a reminder when it fires, and mention that it is a reminder depending on the time gap between setting and firing; include recent context in reminder text if appropriate)",
|
||||
"- sessions_list: list sessions",
|
||||
"- sessions_history: fetch session history",
|
||||
|
||||
@@ -140,10 +140,6 @@
|
||||
"screen_record": {
|
||||
"label": "screen record",
|
||||
"detailKeys": ["node", "nodeId", "duration", "durationMs", "fps", "screenIndex"]
|
||||
},
|
||||
"invoke": {
|
||||
"label": "invoke",
|
||||
"detailKeys": ["node", "nodeId", "invokeCommand"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -37,7 +37,6 @@ const NODES_TOOL_ACTIONS = [
|
||||
"screen_record",
|
||||
"location_get",
|
||||
"run",
|
||||
"invoke",
|
||||
] as const;
|
||||
|
||||
const NOTIFY_PRIORITIES = ["passive", "active", "timeSensitive"] as const;
|
||||
@@ -85,9 +84,6 @@ const NodesToolSchema = Type.Object({
|
||||
commandTimeoutMs: Type.Optional(Type.Number()),
|
||||
invokeTimeoutMs: Type.Optional(Type.Number()),
|
||||
needsScreenRecording: Type.Optional(Type.Boolean()),
|
||||
// invoke
|
||||
invokeCommand: Type.Optional(Type.String()),
|
||||
invokeParamsJson: Type.Optional(Type.String()),
|
||||
});
|
||||
|
||||
export function createNodesTool(options?: {
|
||||
@@ -103,7 +99,7 @@ export function createNodesTool(options?: {
|
||||
label: "Nodes",
|
||||
name: "nodes",
|
||||
description:
|
||||
"Discover and control paired nodes (status/describe/pairing/notify/camera/screen/location/run/invoke).",
|
||||
"Discover and control paired nodes (status/describe/pairing/notify/camera/screen/location/run).",
|
||||
parameters: NodesToolSchema,
|
||||
execute: async (_toolCallId, args) => {
|
||||
const params = args as Record<string, unknown>;
|
||||
@@ -442,31 +438,6 @@ export function createNodesTool(options?: {
|
||||
});
|
||||
return jsonResult(raw?.payload ?? {});
|
||||
}
|
||||
case "invoke": {
|
||||
const node = readStringParam(params, "node", { required: true });
|
||||
const nodeId = await resolveNodeId(gatewayOpts, node);
|
||||
const invokeCommand = readStringParam(params, "invokeCommand", { required: true });
|
||||
const invokeParamsJson =
|
||||
typeof params.invokeParamsJson === "string" ? params.invokeParamsJson.trim() : "";
|
||||
let invokeParams: unknown = {};
|
||||
if (invokeParamsJson) {
|
||||
try {
|
||||
invokeParams = JSON.parse(invokeParamsJson);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
throw new Error(`invokeParamsJson must be valid JSON: ${message}`, { cause: err });
|
||||
}
|
||||
}
|
||||
const invokeTimeoutMs = parseTimeoutMs(params.invokeTimeoutMs);
|
||||
const raw = await callGatewayTool("node.invoke", gatewayOpts, {
|
||||
nodeId,
|
||||
command: invokeCommand,
|
||||
params: invokeParams,
|
||||
timeoutMs: invokeTimeoutMs,
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
});
|
||||
return jsonResult(raw ?? {});
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unknown action: ${action}`);
|
||||
}
|
||||
|
||||
@@ -273,28 +273,6 @@ function buildChatCommands(): ChatCommandDefinition[] {
|
||||
],
|
||||
argsMenu: "auto",
|
||||
}),
|
||||
defineChatCommand({
|
||||
key: "ptt",
|
||||
nativeName: "ptt",
|
||||
description: "Push-to-talk controls for a paired node.",
|
||||
textAlias: "/ptt",
|
||||
acceptsArgs: true,
|
||||
argsParsing: "none",
|
||||
category: "tools",
|
||||
args: [
|
||||
{
|
||||
name: "action",
|
||||
description: "start, stop, once, or cancel",
|
||||
type: "string",
|
||||
choices: ["start", "stop", "once", "cancel"],
|
||||
},
|
||||
{
|
||||
name: "node",
|
||||
description: "node=<id> (optional)",
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
}),
|
||||
defineChatCommand({
|
||||
key: "config",
|
||||
nativeName: "config",
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
} from "./commands-info.js";
|
||||
import { handleModelsCommand } from "./commands-models.js";
|
||||
import { handlePluginCommand } from "./commands-plugin.js";
|
||||
import { handlePTTCommand } from "./commands-ptt.js";
|
||||
import {
|
||||
handleAbortTrigger,
|
||||
handleActivationCommand,
|
||||
@@ -47,7 +46,6 @@ export async function handleCommands(params: HandleCommandsParams): Promise<Comm
|
||||
handleUsageCommand,
|
||||
handleRestartCommand,
|
||||
handleTtsCommands,
|
||||
handlePTTCommand,
|
||||
handleHelpCommand,
|
||||
handleCommandsListCommand,
|
||||
handleStatusCommand,
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { MsgContext } from "../templating.js";
|
||||
import { buildCommandContext, handleCommands } from "./commands.js";
|
||||
import { parseInlineDirectives } from "./directive-handling.js";
|
||||
|
||||
const callGateway = vi.fn(async (_opts: { method?: string }) => ({ ok: true }));
|
||||
|
||||
vi.mock("../../gateway/call.js", () => ({
|
||||
callGateway: (opts: unknown) => callGateway(opts as { method?: string }),
|
||||
randomIdempotencyKey: () => "idem-test",
|
||||
}));
|
||||
|
||||
function buildParams(commandBody: string, cfg: OpenClawConfig, ctxOverrides?: Partial<MsgContext>) {
|
||||
const ctx = {
|
||||
Body: commandBody,
|
||||
CommandBody: commandBody,
|
||||
CommandSource: "text",
|
||||
CommandAuthorized: true,
|
||||
Provider: "telegram",
|
||||
Surface: "telegram",
|
||||
...ctxOverrides,
|
||||
} as MsgContext;
|
||||
|
||||
const command = buildCommandContext({
|
||||
ctx,
|
||||
cfg,
|
||||
isGroup: false,
|
||||
triggerBodyNormalized: commandBody.trim().toLowerCase(),
|
||||
commandAuthorized: true,
|
||||
});
|
||||
|
||||
return {
|
||||
ctx,
|
||||
cfg,
|
||||
command,
|
||||
directives: parseInlineDirectives(commandBody),
|
||||
elevated: { enabled: true, allowed: true, failures: [] },
|
||||
sessionKey: "agent:main:main",
|
||||
workspaceDir: "/tmp",
|
||||
defaultGroupActivation: () => "mention",
|
||||
resolvedVerboseLevel: "off" as const,
|
||||
resolvedReasoningLevel: "off" as const,
|
||||
resolveDefaultThinkingLevel: async () => undefined,
|
||||
provider: "telegram",
|
||||
model: "test-model",
|
||||
contextTokens: 0,
|
||||
isGroup: false,
|
||||
};
|
||||
}
|
||||
|
||||
describe("handleCommands /ptt", () => {
|
||||
it("invokes talk.ptt.once on the default iOS node", async () => {
|
||||
callGateway.mockImplementation(async (opts: { method?: string; params?: unknown }) => {
|
||||
if (opts.method === "node.list") {
|
||||
return {
|
||||
nodes: [
|
||||
{
|
||||
nodeId: "ios-1",
|
||||
displayName: "iPhone",
|
||||
platform: "ios",
|
||||
connected: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
if (opts.method === "node.invoke") {
|
||||
return {
|
||||
ok: true,
|
||||
nodeId: "ios-1",
|
||||
command: "talk.ptt.once",
|
||||
payload: { status: "offline" },
|
||||
};
|
||||
}
|
||||
return { ok: true };
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
commands: { text: true },
|
||||
channels: { telegram: { allowFrom: ["*"] } },
|
||||
} as OpenClawConfig;
|
||||
const params = buildParams("/ptt once", cfg);
|
||||
const result = await handleCommands(params);
|
||||
|
||||
expect(result.shouldContinue).toBe(false);
|
||||
expect(result.reply?.text).toContain("PTT once");
|
||||
expect(result.reply?.text).toContain("status: offline");
|
||||
|
||||
const invokeCall = callGateway.mock.calls.find((call) => call[0]?.method === "node.invoke");
|
||||
expect(invokeCall).toBeTruthy();
|
||||
expect(invokeCall?.[0]?.params?.command).toBe("talk.ptt.once");
|
||||
expect(invokeCall?.[0]?.params?.idempotencyKey).toBe("idem-test");
|
||||
});
|
||||
});
|
||||
@@ -1,208 +0,0 @@
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { CommandHandler } from "./commands-types.js";
|
||||
import { callGateway, randomIdempotencyKey } from "../../gateway/call.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
|
||||
type NodeSummary = {
|
||||
nodeId: string;
|
||||
displayName?: string;
|
||||
platform?: string;
|
||||
deviceFamily?: string;
|
||||
remoteIp?: string;
|
||||
connected?: boolean;
|
||||
};
|
||||
|
||||
const PTT_COMMANDS: Record<string, string> = {
|
||||
start: "talk.ptt.start",
|
||||
stop: "talk.ptt.stop",
|
||||
once: "talk.ptt.once",
|
||||
cancel: "talk.ptt.cancel",
|
||||
};
|
||||
|
||||
function normalizeNodeKey(value: string) {
|
||||
return value
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
.replace(/^-+/, "")
|
||||
.replace(/-+$/, "");
|
||||
}
|
||||
|
||||
function isIOSNode(node: NodeSummary): boolean {
|
||||
const platform = node.platform?.toLowerCase() ?? "";
|
||||
const family = node.deviceFamily?.toLowerCase() ?? "";
|
||||
return (
|
||||
platform.startsWith("ios") ||
|
||||
family.includes("iphone") ||
|
||||
family.includes("ipad") ||
|
||||
family.includes("ios")
|
||||
);
|
||||
}
|
||||
|
||||
async function loadNodes(cfg: OpenClawConfig): Promise<NodeSummary[]> {
|
||||
try {
|
||||
const res = await callGateway<{ nodes?: NodeSummary[] }>({
|
||||
method: "node.list",
|
||||
params: {},
|
||||
config: cfg,
|
||||
});
|
||||
return Array.isArray(res.nodes) ? res.nodes : [];
|
||||
} catch {
|
||||
const res = await callGateway<{ pending?: unknown[]; paired?: NodeSummary[] }>({
|
||||
method: "node.pair.list",
|
||||
params: {},
|
||||
config: cfg,
|
||||
});
|
||||
return Array.isArray(res.paired) ? res.paired : [];
|
||||
}
|
||||
}
|
||||
|
||||
function describeNodes(nodes: NodeSummary[]) {
|
||||
return nodes
|
||||
.map((node) => node.displayName || node.remoteIp || node.nodeId)
|
||||
.filter(Boolean)
|
||||
.join(", ");
|
||||
}
|
||||
|
||||
function resolveNodeId(nodes: NodeSummary[], query?: string): string {
|
||||
const trimmed = String(query ?? "").trim();
|
||||
if (trimmed) {
|
||||
const qNorm = normalizeNodeKey(trimmed);
|
||||
const matches = nodes.filter((node) => {
|
||||
if (node.nodeId === trimmed) {
|
||||
return true;
|
||||
}
|
||||
if (typeof node.remoteIp === "string" && node.remoteIp === trimmed) {
|
||||
return true;
|
||||
}
|
||||
const name = typeof node.displayName === "string" ? node.displayName : "";
|
||||
if (name && normalizeNodeKey(name) === qNorm) {
|
||||
return true;
|
||||
}
|
||||
if (trimmed.length >= 6 && node.nodeId.startsWith(trimmed)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (matches.length === 1) {
|
||||
return matches[0].nodeId;
|
||||
}
|
||||
const known = describeNodes(nodes);
|
||||
if (matches.length === 0) {
|
||||
throw new Error(`unknown node: ${trimmed}${known ? ` (known: ${known})` : ""}`);
|
||||
}
|
||||
throw new Error(
|
||||
`ambiguous node: ${trimmed} (matches: ${matches
|
||||
.map((node) => node.displayName || node.remoteIp || node.nodeId)
|
||||
.join(", ")})`,
|
||||
);
|
||||
}
|
||||
|
||||
const iosNodes = nodes.filter(isIOSNode);
|
||||
const iosConnected = iosNodes.filter((node) => node.connected);
|
||||
const iosCandidates = iosConnected.length > 0 ? iosConnected : iosNodes;
|
||||
if (iosCandidates.length === 1) {
|
||||
return iosCandidates[0].nodeId;
|
||||
}
|
||||
if (iosCandidates.length > 1) {
|
||||
throw new Error(
|
||||
`multiple iOS nodes found (${describeNodes(iosCandidates)}); specify node=<id>`,
|
||||
);
|
||||
}
|
||||
|
||||
const connected = nodes.filter((node) => node.connected);
|
||||
const fallback = connected.length > 0 ? connected : nodes;
|
||||
if (fallback.length === 1) {
|
||||
return fallback[0].nodeId;
|
||||
}
|
||||
|
||||
const known = describeNodes(nodes);
|
||||
throw new Error(`node required${known ? ` (known: ${known})` : ""}`);
|
||||
}
|
||||
|
||||
function parsePTTArgs(commandBody: string) {
|
||||
const tokens = commandBody.trim().split(/\s+/).slice(1);
|
||||
let action: string | undefined;
|
||||
let node: string | undefined;
|
||||
for (const token of tokens) {
|
||||
if (!token) {
|
||||
continue;
|
||||
}
|
||||
if (token.toLowerCase().startsWith("node=")) {
|
||||
node = token.slice("node=".length);
|
||||
continue;
|
||||
}
|
||||
if (!action) {
|
||||
action = token;
|
||||
}
|
||||
}
|
||||
return { action, node };
|
||||
}
|
||||
|
||||
function buildPTTHelpText() {
|
||||
return [
|
||||
"Usage: /ptt <start|stop|once|cancel> [node=<id>]",
|
||||
"Example: /ptt once node=iphone",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export const handlePTTCommand: CommandHandler = async (params, allowTextCommands) => {
|
||||
if (!allowTextCommands) {
|
||||
return null;
|
||||
}
|
||||
const { command, cfg } = params;
|
||||
const normalized = command.commandBodyNormalized.trim();
|
||||
if (!normalized.startsWith("/ptt")) {
|
||||
return null;
|
||||
}
|
||||
if (!command.isAuthorizedSender) {
|
||||
logVerbose(`Ignoring /ptt from unauthorized sender: ${command.senderId || "<unknown>"}`);
|
||||
return { shouldContinue: false, reply: { text: "PTT requires an authorized sender." } };
|
||||
}
|
||||
|
||||
const parsed = parsePTTArgs(normalized);
|
||||
const actionKey = parsed.action?.trim().toLowerCase() ?? "";
|
||||
const commandId = PTT_COMMANDS[actionKey];
|
||||
if (!commandId) {
|
||||
return { shouldContinue: false, reply: { text: buildPTTHelpText() } };
|
||||
}
|
||||
|
||||
try {
|
||||
const nodes = await loadNodes(cfg);
|
||||
const nodeId = resolveNodeId(nodes, parsed.node);
|
||||
const invokeParams: Record<string, unknown> = {
|
||||
nodeId,
|
||||
command: commandId,
|
||||
params: {},
|
||||
idempotencyKey: randomIdempotencyKey(),
|
||||
timeoutMs: 15_000,
|
||||
};
|
||||
const res = await callGateway<{
|
||||
ok?: boolean;
|
||||
payload?: Record<string, unknown>;
|
||||
command?: string;
|
||||
nodeId?: string;
|
||||
}>({
|
||||
method: "node.invoke",
|
||||
params: invokeParams,
|
||||
config: cfg,
|
||||
});
|
||||
const payload = res.payload && typeof res.payload === "object" ? res.payload : {};
|
||||
|
||||
const lines = [`PTT ${actionKey} → ${nodeId}`];
|
||||
if (typeof payload.status === "string") {
|
||||
lines.push(`status: ${payload.status}`);
|
||||
}
|
||||
if (typeof payload.captureId === "string") {
|
||||
lines.push(`captureId: ${payload.captureId}`);
|
||||
}
|
||||
if (typeof payload.transcript === "string" && payload.transcript.trim()) {
|
||||
lines.push(`transcript: ${payload.transcript}`);
|
||||
}
|
||||
|
||||
return { shouldContinue: false, reply: { text: lines.join("\n") } };
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
return { shouldContinue: false, reply: { text: `PTT failed: ${message}` } };
|
||||
}
|
||||
};
|
||||
@@ -251,23 +251,4 @@ describe("nodes-cli coverage", () => {
|
||||
});
|
||||
expect(invoke?.params?.timeoutMs).toBe(6000);
|
||||
});
|
||||
|
||||
it("invokes talk.ptt.once via nodes talk ptt once", async () => {
|
||||
runtimeLogs.length = 0;
|
||||
runtimeErrors.length = 0;
|
||||
callGateway.mockClear();
|
||||
randomIdempotencyKey.mockClear();
|
||||
|
||||
const { registerNodesCli } = await import("./nodes-cli.js");
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerNodesCli(program);
|
||||
|
||||
await program.parseAsync(["nodes", "talk", "ptt", "once", "--node", "mac-1"], { from: "user" });
|
||||
|
||||
const invoke = callGateway.mock.calls.find((call) => call[0]?.method === "node.invoke")?.[0];
|
||||
expect(invoke).toBeTruthy();
|
||||
expect(invoke?.params?.command).toBe("talk.ptt.once");
|
||||
expect(invoke?.params?.idempotencyKey).toBe("rk_test");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
import type { Command } from "commander";
|
||||
import type { NodesRpcOpts } from "./types.js";
|
||||
import { randomIdempotencyKey } from "../../gateway/call.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { runNodesCommand } from "./cli-utils.js";
|
||||
import { callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js";
|
||||
|
||||
type PTTAction = {
|
||||
name: string;
|
||||
command: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
const PTT_ACTIONS: PTTAction[] = [
|
||||
{ name: "start", command: "talk.ptt.start", description: "Start push-to-talk capture" },
|
||||
{ name: "stop", command: "talk.ptt.stop", description: "Stop push-to-talk capture" },
|
||||
{ name: "once", command: "talk.ptt.once", description: "Run push-to-talk once" },
|
||||
{ name: "cancel", command: "talk.ptt.cancel", description: "Cancel push-to-talk capture" },
|
||||
];
|
||||
|
||||
export function registerNodesTalkCommands(nodes: Command) {
|
||||
const talk = nodes.command("talk").description("Talk/voice controls on a paired node");
|
||||
const ptt = talk.command("ptt").description("Push-to-talk controls");
|
||||
|
||||
for (const action of PTT_ACTIONS) {
|
||||
nodesCallOpts(
|
||||
ptt
|
||||
.command(action.name)
|
||||
.description(action.description)
|
||||
.requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
|
||||
.option("--invoke-timeout <ms>", "Node invoke timeout in ms (default 15000)", "15000")
|
||||
.action(async (opts: NodesRpcOpts) => {
|
||||
await runNodesCommand(`talk ptt ${action.name}`, async () => {
|
||||
const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
|
||||
const invokeTimeoutMs = opts.invokeTimeout
|
||||
? Number.parseInt(String(opts.invokeTimeout), 10)
|
||||
: undefined;
|
||||
|
||||
const invokeParams: Record<string, unknown> = {
|
||||
nodeId,
|
||||
command: action.command,
|
||||
params: {},
|
||||
idempotencyKey: randomIdempotencyKey(),
|
||||
};
|
||||
if (typeof invokeTimeoutMs === "number" && Number.isFinite(invokeTimeoutMs)) {
|
||||
invokeParams.timeoutMs = invokeTimeoutMs;
|
||||
}
|
||||
|
||||
const raw = await callGatewayCli("node.invoke", opts, invokeParams);
|
||||
const res =
|
||||
typeof raw === "object" && raw !== null ? (raw as { payload?: unknown }) : {};
|
||||
const payload =
|
||||
res.payload && typeof res.payload === "object"
|
||||
? (res.payload as Record<string, unknown>)
|
||||
: {};
|
||||
|
||||
if (opts.json) {
|
||||
defaultRuntime.log(JSON.stringify(payload, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
const lines = [`PTT ${action.name} → ${nodeId}`];
|
||||
if (typeof payload.status === "string") {
|
||||
lines.push(`status: ${payload.status}`);
|
||||
}
|
||||
if (typeof payload.captureId === "string") {
|
||||
lines.push(`captureId: ${payload.captureId}`);
|
||||
}
|
||||
if (typeof payload.transcript === "string" && payload.transcript.trim()) {
|
||||
lines.push(`transcript: ${payload.transcript}`);
|
||||
}
|
||||
|
||||
defaultRuntime.log(lines.join("\n"));
|
||||
});
|
||||
}),
|
||||
{ timeoutMs: 30_000 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import { registerNodesNotifyCommand } from "./register.notify.js";
|
||||
import { registerNodesPairingCommands } from "./register.pairing.js";
|
||||
import { registerNodesScreenCommands } from "./register.screen.js";
|
||||
import { registerNodesStatusCommands } from "./register.status.js";
|
||||
import { registerNodesTalkCommands } from "./register.talk.js";
|
||||
|
||||
export function registerNodesCli(program: Command) {
|
||||
const nodes = program
|
||||
@@ -29,5 +28,4 @@ export function registerNodesCli(program: Command) {
|
||||
registerNodesCameraCommands(nodes);
|
||||
registerNodesScreenCommands(nodes);
|
||||
registerNodesLocationCommands(nodes);
|
||||
registerNodesTalkCommands(nodes);
|
||||
}
|
||||
|
||||
@@ -20,24 +20,6 @@ const LOCATION_COMMANDS = ["location.get"];
|
||||
|
||||
const SMS_COMMANDS = ["sms.send"];
|
||||
|
||||
const DEVICE_COMMANDS = ["device.status", "device.info"];
|
||||
|
||||
const PHOTOS_COMMANDS = ["photos.latest"];
|
||||
|
||||
const CONTACTS_COMMANDS = ["contacts.search", "contacts.add"];
|
||||
|
||||
const CALENDAR_COMMANDS = ["calendar.events", "calendar.add"];
|
||||
|
||||
const REMINDERS_COMMANDS = ["reminders.list", "reminders.add"];
|
||||
|
||||
const MOTION_COMMANDS = ["motion.activity", "motion.pedometer"];
|
||||
|
||||
const SYSTEM_NOTIFY_COMMANDS = ["system.notify"];
|
||||
|
||||
const CHAT_COMMANDS = ["chat.push"];
|
||||
|
||||
const TALK_COMMANDS = ["talk.ptt.start", "talk.ptt.stop", "talk.ptt.cancel", "talk.ptt.once"];
|
||||
|
||||
const SYSTEM_COMMANDS = [
|
||||
"system.run",
|
||||
"system.which",
|
||||
@@ -48,21 +30,7 @@ const SYSTEM_COMMANDS = [
|
||||
];
|
||||
|
||||
const PLATFORM_DEFAULTS: Record<string, string[]> = {
|
||||
ios: [
|
||||
...CANVAS_COMMANDS,
|
||||
...CAMERA_COMMANDS,
|
||||
...SCREEN_COMMANDS,
|
||||
...LOCATION_COMMANDS,
|
||||
...SYSTEM_NOTIFY_COMMANDS,
|
||||
...CHAT_COMMANDS,
|
||||
...DEVICE_COMMANDS,
|
||||
...PHOTOS_COMMANDS,
|
||||
...CONTACTS_COMMANDS,
|
||||
...CALENDAR_COMMANDS,
|
||||
...REMINDERS_COMMANDS,
|
||||
...MOTION_COMMANDS,
|
||||
...TALK_COMMANDS,
|
||||
],
|
||||
ios: [...CANVAS_COMMANDS, ...CAMERA_COMMANDS, ...SCREEN_COMMANDS, ...LOCATION_COMMANDS],
|
||||
android: [
|
||||
...CANVAS_COMMANDS,
|
||||
...CAMERA_COMMANDS,
|
||||
|
||||
Reference in New Issue
Block a user