opencharm 0.6.2 → 0.7.0
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 +19 -19
- package/dist/main.mjs +33 -9
- package/package.json +1 -1
- package/sim/charm_sim.wasm +0 -0
package/README.md
CHANGED
|
@@ -6,25 +6,25 @@ The command line for [OpenCharm](https://opencharm.dev): the open-source charm f
|
|
|
6
6
|
npx opencharm
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
| Command | What it does
|
|
10
|
-
| ----------------------------------------------------------------------------------------------------- |
|
|
11
|
-
| `opencharm` | Says hello with a blinking face
|
|
12
|
-
| `opencharm faces` | Lists all 22 moods
|
|
13
|
-
| `opencharm face [mood]` | Shows one mood (random if omitted), e.g. `opencharm face learned`
|
|
14
|
-
| `opencharm states` | What your agent is doing, and the face for each state
|
|
15
|
-
| `opencharm hardware` | What to buy and print
|
|
16
|
-
| `opencharm init [dir]` | Clone the starter workspace ([opencharm-starter](https://github.com/opencharm-labs/opencharm-starter)) and fit it to this machine (`--agent claude` (default), `codex`, `gemini`, `goose`, `hermes`, `openclaw`; `--from <git url>` for a fork; `--name <name>` and `--colour <colour>` write the charm's identity, the `charm` block) |
|
|
17
|
-
| `opencharm serve` | Start charmd, the charm daemon, next to your agent (`--config <file>`)
|
|
18
|
-
| `opencharm sim` | The charm on screen, in your browser, talking to charmd (`--url`, `--port`, `--no-open`)
|
|
19
|
-
| `sudo opencharm setup --domain <d>` | Install charmd as a hardened systemd service on a Linux server (`--dry-run` prints the plan; see `docs/deploy.md`)
|
|
20
|
-
| `opencharm pair <code>` | Pair the charm showing that code; you choose its PIN (`--name <name>`)
|
|
21
|
-
| `opencharm status` | What's running (charmd, each connected charm's build, the starter commit of the workspace; spec 015), then the paired charms and whether they are connected, locked or blocked
|
|
22
|
-
| `opencharm lock` / `unlock` / `revoke <charm>` | Lock it now; clear a block and unlock; forget it
|
|
23
|
-
| `opencharm replies on\|off` | The desktop charm speaks its replies, or shows them as text, from the next one; a charm on a board always speaks. To keep it, set `"speakReplies": false` in `opencharm.json`
|
|
24
|
-
| `opencharm look` | Show the charm's look, or change it on every unlocked charm until charmd restarts (`--colour <colour>`, `--greeting <text>`, `--motion full\|calm`); to keep it, set `charm` in `opencharm.json`
|
|
25
|
-
| `opencharm dev face <charm> <state> [text]` / `dev say <charm> <text>` / `dev ask <charm> <question>` | Show a face, speak, or ask a question (hold = yes, press = no) on a connected charm (testing)
|
|
26
|
-
| `opencharm voice` / `voice install` | The local voice's models (Parakeet listening, Supertonic speaking): whether they're here, and download them now (charmd does it on its first start otherwise)
|
|
27
|
-
| `opencharm mcp` | The charm's tools for an agent (MCP over stdio): `say`, `show_face`, `ask`, `notify`, `set_look`. charmd adds it to ACP agents by itself. Other agents running as your user can add `opencharm mcp --config <file>` to their MCP settings; never an agent running as another user (it reaches charmd's admin socket)
|
|
9
|
+
| Command | What it does |
|
|
10
|
+
| ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| `opencharm` | Says hello with a blinking face |
|
|
12
|
+
| `opencharm faces` | Lists all 22 moods |
|
|
13
|
+
| `opencharm face [mood]` | Shows one mood (random if omitted), e.g. `opencharm face learned` |
|
|
14
|
+
| `opencharm states` | What your agent is doing, and the face for each state |
|
|
15
|
+
| `opencharm hardware` | What to buy and print |
|
|
16
|
+
| `opencharm init [dir]` | Clone the starter workspace ([opencharm-starter](https://github.com/opencharm-labs/opencharm-starter)) and fit it to this machine (`--agent claude` (default), `codex`, `gemini`, `goose`, `hermes`, `openclaw`; `--from <git url>` for a fork; `--name <name>` and `--colour <colour>` (one of the six, or `#RRGGBB`) write the charm's identity, the `charm` block) |
|
|
17
|
+
| `opencharm serve` | Start charmd, the charm daemon, next to your agent (`--config <file>`) |
|
|
18
|
+
| `opencharm sim` | The charm on screen, in your browser, talking to charmd (`--url`, `--port`, `--no-open`) |
|
|
19
|
+
| `sudo opencharm setup --domain <d>` | Install charmd as a hardened systemd service on a Linux server (`--dry-run` prints the plan; see `docs/deploy.md`) |
|
|
20
|
+
| `opencharm pair <code>` | Pair the charm showing that code; you choose its PIN (`--name <name>`) |
|
|
21
|
+
| `opencharm status` | What's running (charmd, each connected charm's build, the starter commit of the workspace; spec 015), then the paired charms and whether they are connected, locked or blocked |
|
|
22
|
+
| `opencharm lock` / `unlock` / `revoke <charm>` | Lock it now; clear a block and unlock; forget it |
|
|
23
|
+
| `opencharm replies on\|off` | The desktop charm speaks its replies, or shows them as text, from the next one; a charm on a board always speaks. To keep it, set `"speakReplies": false` in `opencharm.json` |
|
|
24
|
+
| `opencharm look` | Show the charm's look, or change it on every unlocked charm until charmd restarts (`--colour <colour>`, `--greeting <text>`, `--motion full\|calm`); to keep it, set `charm` in `opencharm.json` |
|
|
25
|
+
| `opencharm dev face <charm> <state> [text]` / `dev say <charm> <text>` / `dev ask <charm> <question>` | Show a face, speak, or ask a question (hold = yes, press = no) on a connected charm (testing) |
|
|
26
|
+
| `opencharm voice` / `voice install` | The local voice's models (Parakeet listening, Supertonic speaking): whether they're here, and download them now (charmd does it on its first start otherwise) |
|
|
27
|
+
| `opencharm mcp` | The charm's tools for an agent (MCP over stdio): `say`, `show_face`, `ask`, `notify`, `set_look`. charmd adds it to ACP agents by itself. Other agents running as your user can add `opencharm mcp --config <file>` to their MCP settings; never an agent running as another user (it reaches charmd's admin socket) |
|
|
28
28
|
|
|
29
29
|
Options: `--colour white|cobalt|lime|lilac|sun|coal`, `--no-anim`, `--no-color` (also respects `NO_COLOR`).
|
|
30
30
|
|
package/dist/main.mjs
CHANGED
|
@@ -629,10 +629,33 @@ const MAX_NAME_CHARS = 12;
|
|
|
629
629
|
const MAX_NAME_BYTES = 24;
|
|
630
630
|
const MAX_GREETING_BYTES = 40;
|
|
631
631
|
const COLOUR_IDS = facesData.colours.map((colour) => colour.id);
|
|
632
|
+
const OWN_COLOUR = /^#[0-9A-Fa-f]{6}$/;
|
|
632
633
|
const lookName = z.string().min(1, "the name can't be empty").refine((name) => [...name].length <= MAX_NAME_CHARS && utf8Length(name) <= MAX_NAME_BYTES, { message: `at most ${MAX_NAME_CHARS} characters` });
|
|
634
|
+
function ownColourProblem(hex) {
|
|
635
|
+
const [r, g, b] = [
|
|
636
|
+
1,
|
|
637
|
+
3,
|
|
638
|
+
5
|
|
639
|
+
].map((at) => Number.parseInt(hex.slice(at, at + 2), 16) / 255);
|
|
640
|
+
const linear = (v) => v <= .04045 ? v / 12.92 : ((v + .055) / 1.055) ** 2.4;
|
|
641
|
+
if (.2126 * linear(r) + .7152 * linear(g) + .0722 * linear(b) < .05) return "too dark to see on black";
|
|
642
|
+
const max = Math.max(r, g, b);
|
|
643
|
+
const spread = max - Math.min(r, g, b);
|
|
644
|
+
if (max !== r || max < .5 || spread / max < .5) return void 0;
|
|
645
|
+
const hue = 60 * (g - b) / spread;
|
|
646
|
+
return hue >= 6 && hue <= 40 ? "too close to the orange that means 'it needs you'" : void 0;
|
|
647
|
+
}
|
|
648
|
+
const colour = z.string().superRefine((value, context) => {
|
|
649
|
+
if (COLOUR_IDS.includes(value)) return;
|
|
650
|
+
const problem = OWN_COLOUR.test(value) ? ownColourProblem(value) : `the colour is one of ${COLOUR_IDS.join(", ")}, or your own as #RRGGBB`;
|
|
651
|
+
if (problem) context.addIssue({
|
|
652
|
+
code: "custom",
|
|
653
|
+
message: problem
|
|
654
|
+
});
|
|
655
|
+
}).transform((value) => OWN_COLOUR.test(value) ? value.toUpperCase() : value);
|
|
633
656
|
const lookFields = {
|
|
634
657
|
name: lookName.optional(),
|
|
635
|
-
colour:
|
|
658
|
+
colour: colour.optional(),
|
|
636
659
|
greeting: z.string().refine((text) => utf8Length(text) <= MAX_GREETING_BYTES, { message: `at most ${MAX_GREETING_BYTES} bytes` }).optional(),
|
|
637
660
|
sleepAfterMinutes: z.number().int().min(0).max(1440).optional(),
|
|
638
661
|
motion: z.enum(["full", "calm"]).optional()
|
|
@@ -642,6 +665,7 @@ function issues(error) {
|
|
|
642
665
|
return error.issues.map((issue) => `${issue.path.join(".") || "look"}: ${issue.message}`).join("; ");
|
|
643
666
|
}
|
|
644
667
|
function glyphOf(colourId) {
|
|
668
|
+
if (OWN_COLOUR.test(colourId)) return colourId;
|
|
645
669
|
const colour = facesData.colours.find((c) => c.id === colourId);
|
|
646
670
|
if (!colour) throw new Error(`Unknown colour "${colourId}"`);
|
|
647
671
|
return colour.g;
|
|
@@ -969,7 +993,7 @@ function sendAdmin(socketPath, request) {
|
|
|
969
993
|
}
|
|
970
994
|
//#endregion
|
|
971
995
|
//#region package.json
|
|
972
|
-
var version = "0.
|
|
996
|
+
var version = "0.7.0";
|
|
973
997
|
function channelToCube(value) {
|
|
974
998
|
if (value < 48) return 0;
|
|
975
999
|
if (value < 115) return 1;
|
|
@@ -1463,7 +1487,7 @@ function runHelp(ctx) {
|
|
|
1463
1487
|
opencharm hardware what to buy and print
|
|
1464
1488
|
|
|
1465
1489
|
charmd, the charm daemon (runs next to your agent)
|
|
1466
|
-
opencharm init [dir] clone the starter workspace for your agent [--agent claude|codex|gemini|goose|hermes|openclaw] [--from <git url>] [--name <name>] [--colour <colour>]
|
|
1490
|
+
opencharm init [dir] clone the starter workspace for your agent [--agent claude|codex|gemini|goose|hermes|openclaw] [--from <git url>] [--name <name>] [--colour <colour|#RRGGBB>]
|
|
1467
1491
|
opencharm setup --domain <d> install charmd as a service (Linux, sudo) [--dry-run]
|
|
1468
1492
|
opencharm serve start charmd [--config <file>]
|
|
1469
1493
|
opencharm sim the charm on screen, in your browser [--url <ws>] [--port <n>]
|
|
@@ -1474,7 +1498,7 @@ function runHelp(ctx) {
|
|
|
1474
1498
|
opencharm unlock <charm> clear a block and unlock
|
|
1475
1499
|
opencharm revoke <charm> forget a charm (it must pair again)
|
|
1476
1500
|
opencharm replies on|off the desktop charm speaks its replies, or shows them as text
|
|
1477
|
-
opencharm look show or change the charm's look until charmd restarts [--colour <c>] [--greeting <text>] [--motion full|calm]
|
|
1501
|
+
opencharm look show or change the charm's look until charmd restarts [--colour <c|#RRGGBB>] [--greeting <text>] [--motion full|calm]
|
|
1478
1502
|
opencharm dev face <charm> <state> [text] show a face (testing)
|
|
1479
1503
|
opencharm dev say <charm> <text> speak through it (testing)
|
|
1480
1504
|
opencharm dev ask <charm> <question> ask it; hold = yes, press = no (testing)
|
|
@@ -1582,7 +1606,7 @@ const VERSIONS = [
|
|
|
1582
1606
|
"2024-11-05"
|
|
1583
1607
|
];
|
|
1584
1608
|
const FACES = facesData.states.map((state) => state.id);
|
|
1585
|
-
const
|
|
1609
|
+
const COLOUR = `^(${facesData.colours.map((colour) => colour.id).join("|")}|#[0-9A-Fa-f]{6})$`;
|
|
1586
1610
|
const line = z.string().min(1).max(200);
|
|
1587
1611
|
const label = z.string().min(1).max(12);
|
|
1588
1612
|
const TOOLS = [
|
|
@@ -1701,8 +1725,8 @@ const TOOLS = [
|
|
|
1701
1725
|
properties: {
|
|
1702
1726
|
colour: {
|
|
1703
1727
|
type: "string",
|
|
1704
|
-
|
|
1705
|
-
description: "cobalt is blue, lime green, lilac purple, sun yellow; white and coal light the glyphs white."
|
|
1728
|
+
pattern: COLOUR,
|
|
1729
|
+
description: "cobalt is blue, lime green, lilac purple, sun yellow; white and coal light the glyphs white. Any other colour as #RRGGBB (not orange, not too dark)."
|
|
1706
1730
|
},
|
|
1707
1731
|
greeting: {
|
|
1708
1732
|
type: "string",
|
|
@@ -1716,7 +1740,7 @@ const TOOLS = [
|
|
|
1716
1740
|
}
|
|
1717
1741
|
},
|
|
1718
1742
|
args: z.object({
|
|
1719
|
-
colour: z.
|
|
1743
|
+
colour: z.string().regex(new RegExp(COLOUR)).optional(),
|
|
1720
1744
|
greeting: z.string().max(40).optional(),
|
|
1721
1745
|
motion: z.enum(["full", "calm"]).optional()
|
|
1722
1746
|
}).strict(),
|
|
@@ -4941,7 +4965,7 @@ function formatIdentity(unit, version, commit) {
|
|
|
4941
4965
|
let cached;
|
|
4942
4966
|
function identity() {
|
|
4943
4967
|
if (cached) return cached;
|
|
4944
|
-
const commit = "
|
|
4968
|
+
const commit = "d601217";
|
|
4945
4969
|
cached = {
|
|
4946
4970
|
version: `cli@${version}`,
|
|
4947
4971
|
commit,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencharm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "An open-source body for the AI agent you already run: a face, a voice, one key. Claude Code, Codex, Gemini CLI, Hermes, OpenClaw or any ACP or OpenAI-compatible agent; on a small board, your Mac's notch or the browser.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/sim/charm_sim.wasm
CHANGED
|
Binary file
|