Skip to content

Commit

Permalink
Continued ClientNode and REPL development (#1308)
Browse files Browse the repository at this point in the history
* Continued ClientNode and REPL development

This builds out the controller for the node API to the point where I need to address a conflict between
InteractionClient and InteractionServer.

Adds:

* ControllerBehavior - initializes the node environment to act as a controller and tracks active discoveries
* Discovery - a set of classes that manage discovery of commissionable devices
* CommissioningClient - a behavior that handles commissioning and related metadata management

Fleshes out ClientNode related API.  Persistence and management of known nodes work now.

Also includes continued development on the "cli tool" REPL including numerous quality of life and aesthetic
improvements.

* Address PR feedback

* Fix discovery cancelation + node hygiene

* Cancelation previously left the timeout timer dangling; fixed
* Added methods on endpoints to "erase" (clear persisted state) and "delete" (erase + close)
* Modified server addresses to include optional "discovery time" and ttl
* Modified ClientNodes to prune addresses and commissionable nodes that expire
* Make Time.sleep cancelable

Adds a few new public APIs but otherwise this is only used by new (under development) codepaths.
  • Loading branch information
lauckhart authored Oct 25, 2024
1 parent 128ee34 commit 35e309e
Show file tree
Hide file tree
Showing 90 changed files with 3,009 additions and 766 deletions.
1 change: 1 addition & 0 deletions packages/cli-tool/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@matter/protocol": "*",
"@matter/tools": "*",
"@matter/types": "*",
"@matter/nodejs": "*",
"@types/escodegen": "^0.0.10",
"acorn": "^8.13.0",
"ansi-colors": "^4.1.3",
Expand Down
1 change: 1 addition & 0 deletions packages/cli-tool/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { repl } from "#repl.js";
import "@matter/nodejs";
import colors from "ansi-colors";
import { stdout } from "process";
import yargs from "yargs";
Expand Down
6 changes: 4 additions & 2 deletions packages/cli-tool/src/commands/cd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import { Command } from "./command.js";

Command({
usage: "[PATH]",
description: "Change current working directory. If you omit PATH changes to the root directory.",
description: "Change current working directory. If you omit PATH changes to the last node entered.",
maxArgs: 1,

invoke: async function cd([path]) {
if (path === undefined) {
path = "/";
path = this.env.vars.get("home", "/");
} else {
path = `${path}`;
}
Expand All @@ -26,5 +26,7 @@ Command({
}

this.location = location;

await this.env.vars.persist("cwd", location.path);
},
});
15 changes: 15 additions & 0 deletions packages/cli-tool/src/commands/clear.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @license
* Copyright 2022-2024 Matter.js Authors
* SPDX-License-Identifier: Apache-2.0
*/

import { Command } from "./command.js";

Command({
description: "Clear the terminal screen",

invoke: function clear() {
console.clear();
},
});
20 changes: 14 additions & 6 deletions packages/cli-tool/src/commands/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ Command({

if (path === undefined) {
const HELP = `This tool allows you to interact with matter.js and your local Matter environment.
This tool understands both JavaScript and a shell-like syntax that maps "commands" to functions. Type ${quote("ls bin")} to see commands you can always use. Type ${quote("help <name>")} for help with a specific command.
This tool understands both JavaScript and a shell-like syntax that maps "commands" to functions. Type ${quote("ls /bin")} to see commands you can always use. Type ${quote("help <name>")} for help with a specific command.
The current path appears in the prompt. This points to the object this tool uses to find commands. It is also the "global" object for any JavaScript statements you enter.
You can change the current path using ${quote("cd <path>")}. Paths work like you would expect, including ${quote("/")}, ${quote(".")} and ${quote("..")}.`;
You can change the current path using ${quote("cd <path>")}. Paths work like you would expect, including ${quote("/")}, ${quote(".")} and ${quote("..")}.\n`;

this.out(
"\nWelcome to ",
Expand All @@ -44,7 +44,7 @@ You can change the current path using ${quote("cd <path>")}. Paths work like yo
const what = await this.searchPathFor(pathStr);

if (what.kind !== "command") {
this.out(`${path} is a ${what} but we can't tell you much more about it.`);
this.out(`${path} is a ${what} but we can't tell you much more about it.\n\n`);
return;
}

Expand All @@ -56,15 +56,23 @@ You can change the current path using ${quote("cd <path>")}. Paths work like yo

let ast;
try {
ast = parse(`${what.definition}`, { ecmaVersion: "latest" }).body[0];
ast = parse(`${what.definition}`, { ecmaVersion: "latest", checkPrivateFields: false }).body[0];
} catch (e) {
if (!(e instanceof SyntaxError)) {
throw e;
}
try {
ast = parse(`function ${what.definition}`, { ecmaVersion: "latest", checkPrivateFields: false })
.body[0];
} catch (e) {
if (!(e instanceof SyntaxError)) {
throw e;
}
}
}

if (ast?.type !== "FunctionDeclaration") {
this.out(`\nWell, ${colors.blue(pathStr)} is a function but we can't seem to parse it.`);
this.out(`\nWell, ${colors.blue(pathStr)} is a function but we can't seem to parse it.\n\n`);
return;
}

Expand All @@ -74,7 +82,7 @@ You can change the current path using ${quote("cd <path>")}. Paths work like yo
}

this.out(
`\nUsage: ${usage.join(" ")}\n\n${colors.blue(pathStr)} is a function that does not provide additional usage details.\n`,
`\nUsage: ${usage.join(" ")}\n\n${colors.blue(pathStr)} is a function that does not provide additional usage details.\n\n`,
);
},
});
Expand Down
1 change: 1 addition & 0 deletions packages/cli-tool/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import "./cat.js";
import "./cd.js";
import "./clear.js";
import "./exit.js";
import "./help.js";
import "./ls.js";
Expand Down
21 changes: 18 additions & 3 deletions packages/cli-tool/src/commands/ls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Command({
flagArgs: {
a: "show hidden properties",
l: "use a long listing format",
d: "list directories themselves, not their contents",
},

invoke: async function ls(args, flags) {
Expand All @@ -30,7 +31,7 @@ Command({

if (locations.length) {
for (const location of locations) {
if (location.kind === "directory") {
if (location.kind === "directory" && !flags.d) {
dirs.push(location);
} else {
files.push(location);
Expand Down Expand Up @@ -169,6 +170,14 @@ function formatName(location: DisplayLocation) {
return { name: `${colors.green(name)}*`, length: length + 1 };
}

if (location.tag === "constructor") {
return { name: `${colors.green.bold(name)}*`, length: length + 1 };
}

if (location.tag === "event") {
return { name: `${colors.yellow(name)}=`, length: length + 1 };
}

return { name, length };
}

Expand Down Expand Up @@ -240,8 +249,14 @@ function DisplayLocation(location: Location, all: boolean, displayName?: string)
function listPaths(paths: string[], definition: unknown) {
const result = new Set(paths);
if (all && typeof definition === "object" && definition !== null) {
for (const key of Object.getOwnPropertyNames(definition)) {
result.add(key);
for (
let obj = definition;
obj !== undefined && obj !== null && obj !== Object.prototype;
obj = Object.getPrototypeOf(obj)
) {
for (const key of Object.getOwnPropertyNames(obj)) {
result.add(key);
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions packages/cli-tool/src/commands/set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Command } from "./command.js";
Command({
usage: ["", "KEY=VALUE", "KEY VALUE"],
description:
'Set or display environment variables. matter.js defines variables in a hierarchy with "." as a delimiter.',
'Set or display environment variables. matter.js defines variables in a hierarchy with "." as a delimiter. Variables persist across restarts.',
maxArgs: 2,

invoke: async function set(args) {
Expand All @@ -24,11 +24,11 @@ Command({
if (equalPos === -1) {
this.err("Invalid argument: parameter must be of the form key=value");
}
this.env.vars.set(assignment.slice(0, equalPos), assignment.slice(equalPos + 1));
await this.env.vars.persist(assignment.slice(0, equalPos), assignment.slice(equalPos + 1));
break;

case 2:
this.env.vars.set(`${args[0]}`, args[1] as VariableService.Value);
await this.env.vars.persist(`${args[0]}`, args[1] as VariableService.Value);
break;
}
},
Expand Down
Loading

0 comments on commit 35e309e

Please sign in to comment.