uc-config 0.1.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/LICENSE +24 -0
- package/README.md +229 -0
- package/dist/adapter.d.ts +26 -0
- package/dist/adapter.js +540 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +555 -0
- package/dist/client.d.ts +25 -0
- package/dist/client.js +182 -0
- package/dist/compiler.d.ts +5 -0
- package/dist/compiler.js +155 -0
- package/dist/diagnose.d.ts +41 -0
- package/dist/diagnose.js +133 -0
- package/dist/dsl.d.ts +67 -0
- package/dist/dsl.js +103 -0
- package/dist/engine.d.ts +32 -0
- package/dist/engine.js +276 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/init.d.ts +7 -0
- package/dist/init.js +124 -0
- package/dist/inventory.d.ts +14 -0
- package/dist/inventory.js +236 -0
- package/dist/model.d.ts +91 -0
- package/dist/model.js +7 -0
- package/dist/planner.d.ts +12 -0
- package/dist/planner.js +248 -0
- package/dist/recovery.d.ts +4 -0
- package/dist/recovery.js +63 -0
- package/dist/schema.d.ts +4 -0
- package/dist/schema.js +75 -0
- package/dist/transport.d.ts +2 -0
- package/dist/transport.js +101 -0
- package/dist/util.d.ts +18 -0
- package/dist/util.js +140 -0
- package/dist/wire.d.ts +17199 -0
- package/docs/cli.md +125 -0
- package/docs/configuration-authoring.md +196 -0
- package/docs/handoff.md +24 -0
- package/docs/snippets.md +312 -0
- package/examples/full-setup.ts +113 -0
- package/package.json +67 -0
- package/remote.config.example.ts +65 -0
- package/vendor/README.md +9 -0
- package/vendor/core-api.json +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Braden Polly
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
The vendored Unfolded Circle Core API specification in vendor/ is licensed
|
|
24
|
+
separately under CC-BY-SA-4.0; see vendor/README.md.
|
package/README.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# uc-config
|
|
2
|
+
|
|
3
|
+
Configuration-as-code for the [Unfolded Circle Remote 3](https://www.unfoldedcircle.com/).
|
|
4
|
+
Describe activities, button mappings, pages, macros, integrations and settings in
|
|
5
|
+
TypeScript, review a plan against the live remote, then apply it over the local
|
|
6
|
+
Core API. Nothing runs on the remote except its own native configuration.
|
|
7
|
+
|
|
8
|
+
**This project is built to be driven by a coding agent** (Claude Code, Codex,
|
|
9
|
+
Cursor, Hermes, etc.). A person states the intent ("make the play button on the
|
|
10
|
+
Apple TV activity skip chapters") and the agent edits config, validates, plans,
|
|
11
|
+
and applies. The guardrails (three-way drift detection, saved plans, write
|
|
12
|
+
journals, read-only diagnostics) exist so an agent can operate safely on real
|
|
13
|
+
hardware. Humans can use it directly too.
|
|
14
|
+
|
|
15
|
+
> Agents: read [AGENTS.md](AGENTS.md) first, then [docs/snippets.md](docs/snippets.md)
|
|
16
|
+
> for copy-paste recipes. Ask the user only for what you cannot discover:
|
|
17
|
+
> the remote's IP address and the web configurator PIN.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
Before you start: install Node.js 22+, and on the remote enable the web
|
|
22
|
+
configurator (Settings → Profile → Web configurator). Note the remote's IP and PIN.
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
mkdir my-remote && cd my-remote
|
|
26
|
+
npx uc-config init # package.json, tsconfig, .gitignore, AGENTS.md
|
|
27
|
+
npm install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then start your coding agent in that folder and prompt:
|
|
31
|
+
|
|
32
|
+
> Set up my Remote 3 at `<IP>`
|
|
33
|
+
|
|
34
|
+
When the agent asks you to authenticate, run `npx uc-config auth` in your own
|
|
35
|
+
terminal and enter the PIN. Don't paste the PIN into chat. That folder is your
|
|
36
|
+
configuration; commit it to a **private** git repo.
|
|
37
|
+
|
|
38
|
+
## Requirements
|
|
39
|
+
|
|
40
|
+
- Node.js 22+ and npm
|
|
41
|
+
- A Remote 3 on the same LAN, with the **web configurator enabled**
|
|
42
|
+
(Settings → Profile → Web configurator) and its PIN
|
|
43
|
+
- Optional but recommended: the [UC Integration Manager](#integration-manager)
|
|
44
|
+
|
|
45
|
+
Tested against Remote 3 core `0.81.x`, API `0.19.0`. Firmware changes are detected
|
|
46
|
+
and block writes until you reconnect.
|
|
47
|
+
|
|
48
|
+
## Setup (agent runbook)
|
|
49
|
+
|
|
50
|
+
Run these in the config workspace (the folder created by `init`) using
|
|
51
|
+
`npx uc-config`, or from a clone of this repo using `npm run uc --`, as below.
|
|
52
|
+
Each step is idempotent or fails safely.
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
# 1. Install and build the CLI (dist/ is required by remote.config.ts imports)
|
|
56
|
+
npm install
|
|
57
|
+
npm run build
|
|
58
|
+
npm test # offline, uses a mock Core API
|
|
59
|
+
|
|
60
|
+
# 2. Register the remote under a target name ("home" here). With a single
|
|
61
|
+
# target, later commands pick it automatically; otherwise pass --target.
|
|
62
|
+
npm run uc -- connect home --host http://<REMOTE_IP>
|
|
63
|
+
|
|
64
|
+
# 3. Authenticate. Prompts for the web-configurator PIN in a TTY.
|
|
65
|
+
# Headless agents: have the user run this step, or pass UC_PIN in the env.
|
|
66
|
+
# Never write the PIN or key into files or chat logs.
|
|
67
|
+
npm run uc -- auth
|
|
68
|
+
|
|
69
|
+
# 4. Verify connectivity and API coverage
|
|
70
|
+
npm run uc -- doctor
|
|
71
|
+
|
|
72
|
+
# 5. Snapshot what is on the remote
|
|
73
|
+
npm run uc -- inventory --bindings generated/devices.ts # entity IDs + commands
|
|
74
|
+
npm run uc -- import --out remote.config.ts # editable config
|
|
75
|
+
npm run uc -- diagnose # health check
|
|
76
|
+
|
|
77
|
+
# 6. Take ownership (local only; writes nothing to the remote)
|
|
78
|
+
npm run uc -- compile
|
|
79
|
+
npm run uc -- plan --out .uc/plan.json # expect only "= adopt" operations
|
|
80
|
+
npm run uc -- apply .uc/plan.json --adopt-only
|
|
81
|
+
|
|
82
|
+
# 7. Confirm convergence
|
|
83
|
+
npm run uc -- check # 0 operations, 0 conflicts
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
After step 7 the workspace is ready. Every later change follows the edit loop below.
|
|
87
|
+
|
|
88
|
+
Notes for agents:
|
|
89
|
+
|
|
90
|
+
- `auth` stores the API key in `.uc/credentials.json` (mode 0600). Alternatively
|
|
91
|
+
set `UC_API_KEY` to an existing key. If a key named `uc-config` already exists
|
|
92
|
+
on the remote, reuse it or revoke it in the configurator first.
|
|
93
|
+
- `inventory`/`import` refuse to overwrite existing files (`wx`). Use new,
|
|
94
|
+
dated filenames when refreshing (e.g. `.uc/devices-2026-10-03.ts`).
|
|
95
|
+
- On macOS, if Node is denied Local Network access the CLI falls back to `curl`
|
|
96
|
+
automatically. If _every_ LAN device is unreachable but the router answers,
|
|
97
|
+
grant Local Network permission to the terminal app that launched the agent.
|
|
98
|
+
- `remote.config.ts`, `generated/` and `.uc/` are gitignored: they describe
|
|
99
|
+
your home and contain credentials. Keep them in a private repo or backup
|
|
100
|
+
(see [Keeping your config private](#keeping-your-config-private)).
|
|
101
|
+
|
|
102
|
+
## The edit loop
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
# edit remote.config.ts
|
|
106
|
+
npm run check:examples # typecheck config + examples
|
|
107
|
+
npm run uc -- compile # evaluate TS -> .uc/build.json (offline)
|
|
108
|
+
npm run uc -- plan --out .uc/plan.json
|
|
109
|
+
# read every operation, conflict and deferred item; nothing unexpected allowed
|
|
110
|
+
npm run uc -- apply .uc/plan.json
|
|
111
|
+
npm run uc -- check # must report 0 operations
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`plan` never writes. `apply` only executes a saved, reviewed plan and re-verifies
|
|
115
|
+
remote identity, firmware and preconditions first. A plan with zero operations
|
|
116
|
+
needs no apply. See [docs/snippets.md](docs/snippets.md) for common edits.
|
|
117
|
+
|
|
118
|
+
## Diagnostics and fixing problems
|
|
119
|
+
|
|
120
|
+
Run `diagnose` whenever something looks wrong on the remote, after any
|
|
121
|
+
integration update, and before large changes:
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
npm run uc -- diagnose # human-readable; exit 2 if problems found
|
|
125
|
+
npm run uc -- diagnose --json # for agents
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
It is read-only and reports:
|
|
129
|
+
|
|
130
|
+
- **Orphaned entity references**: activities/macros pointing at an entity that
|
|
131
|
+
no longer exists (the remote shows these as "orphaned" or "unavailable").
|
|
132
|
+
- **Disconnected integrations**: any integration not in `CONNECTED` state.
|
|
133
|
+
|
|
134
|
+
For each orphan it proposes a fix:
|
|
135
|
+
|
|
136
|
+
| Diagnosis | Cause | Fix |
|
|
137
|
+
| ---------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
138
|
+
| Integration still offers the same entity | Integration update/reinstall dropped configured entities | Re-add it with the printed `api POST ... --write` command, then `plan` (expect 0 ops) |
|
|
139
|
+
| Integration offers a _different_ entity of same type | Driver re-keyed the device (e.g. MAC → serial ID) | Re-add the candidate, replace the old ID **everywhere** in `remote.config.ts`, plan, apply |
|
|
140
|
+
| Integration not `CONNECTED` | Device offline, credentials changed, driver crashed | Reconnect in the configurator or Integration Manager, rerun `diagnose` |
|
|
141
|
+
| No integration owns the ID | Integration removed | Reinstall it (often via the Integration Manager) or remove the references |
|
|
142
|
+
|
|
143
|
+
Plan/apply problems:
|
|
144
|
+
|
|
145
|
+
| Message | Meaning and fix |
|
|
146
|
+
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
147
|
+
| `Drift at <fields>` | Someone (or a driver update) changed an owned field on the remote. Copy the live value into source, or rerun `plan --overwrite-drift` only if the user wants the source value restored. |
|
|
148
|
+
| `Managed resource disappeared` | Resource was deleted on the remote. Run `diagnose`; re-add or remove from source. |
|
|
149
|
+
| `Cannot verify command` | `cmd_id` isn't advertised by that entity, or the entity isn't in the activity's `entity_ids`. Check `generated/devices.ts`, refresh inventory. |
|
|
150
|
+
| `Remote firmware changed; reconnect and replan` | Remote auto-updated. Rerun `connect <name> --host ...` (keeps credentials), then plan. |
|
|
151
|
+
| `transport failed` / timeouts | Remote asleep (wake it) or no LAN access from this machine. |
|
|
152
|
+
| Uncertain writes after a crash | Run `resume`. Never blindly rerun apply; use `state adopt KEY ID` if a create actually succeeded. |
|
|
153
|
+
|
|
154
|
+
`npm run uc -- api GET <path>` makes a raw authenticated Core API read (output
|
|
155
|
+
redacted). Non-GET methods require `--write` and should only be used for the
|
|
156
|
+
targeted repairs above.
|
|
157
|
+
|
|
158
|
+
Exit codes: `0` ok, `1` error, `2` drift/conflicts/deferred work/diagnose
|
|
159
|
+
findings, `3` paused waiting for a human step (setup or pairing).
|
|
160
|
+
|
|
161
|
+
## Integration Manager
|
|
162
|
+
|
|
163
|
+
Some things cannot be done through uc-config alone and need the community
|
|
164
|
+
[**UC Integration Manager**](https://github.com/JackJPowell/uc-intg-manager)
|
|
165
|
+
(runs on the remote at `http://<REMOTE_IP>:9999` while docked, or in Docker):
|
|
166
|
+
|
|
167
|
+
- **Installing custom/community integrations** (Kaleidescape, Oppo, Onkyo/Integra,
|
|
168
|
+
Lutron, Kodi, etc.) from the community registry or a GitHub release. uc-config
|
|
169
|
+
can register an external driver (`driver()`) or upload a local archive
|
|
170
|
+
(`driverArchive()`), but it does not browse, download or version community
|
|
171
|
+
integrations.
|
|
172
|
+
- **Updating integrations**, selecting versions, and rolling back.
|
|
173
|
+
- **Backing up integration configuration** (driver setup data the Core API does
|
|
174
|
+
not export; `import` cannot recover these).
|
|
175
|
+
- **UI diagnostics**: orphaned entities, orphaned IR codesets and unused
|
|
176
|
+
activity entities, plus integration logs.
|
|
177
|
+
|
|
178
|
+
Recommended split: install and update integrations with the Integration Manager,
|
|
179
|
+
then let uc-config own everything that _uses_ them (entities, activities,
|
|
180
|
+
buttons, pages, macros).
|
|
181
|
+
|
|
182
|
+
> **Integration updates can drop configured entities**, even when the manager
|
|
183
|
+
> says configuration is preserved. Every activity using them becomes orphaned.
|
|
184
|
+
> Keep the manager's _Auto update_ setting off, and after every integration
|
|
185
|
+
> update run:
|
|
186
|
+
>
|
|
187
|
+
> ```sh
|
|
188
|
+
> npm run uc -- diagnose && npm run uc -- plan
|
|
189
|
+
> ```
|
|
190
|
+
>
|
|
191
|
+
> Updates marked as _not preserving configuration_ additionally require
|
|
192
|
+
> re-running that integration's setup.
|
|
193
|
+
|
|
194
|
+
## Keeping your config private
|
|
195
|
+
|
|
196
|
+
This repo holds the tool. Your configuration belongs in its own folder, made
|
|
197
|
+
with `npx uc-config init` (see [Quick start](#quick-start)), in a **private**
|
|
198
|
+
git repo. `init` gitignores `.uc/`, which holds credentials.
|
|
199
|
+
|
|
200
|
+
Alternatively, use `--workspace <dir>` to point the CLI at any directory holding
|
|
201
|
+
`remote.config.ts` and `.uc/`. Back up `.uc/state` and `.uc/journals`
|
|
202
|
+
privately; losing them makes the next plan an adoption pass.
|
|
203
|
+
|
|
204
|
+
## Reference
|
|
205
|
+
|
|
206
|
+
- [AGENTS.md](AGENTS.md): rules for coding agents working with this repo
|
|
207
|
+
- [docs/snippets.md](docs/snippets.md): common tasks as copy-paste recipes
|
|
208
|
+
- [docs/configuration-authoring.md](docs/configuration-authoring.md): config syntax, ownership, helpers
|
|
209
|
+
- [docs/cli.md](docs/cli.md): every CLI command, provisioning, recovery
|
|
210
|
+
- [examples/full-setup.ts](examples/full-setup.ts): integrations, docks, IR/BT remotes, pairing, profiles
|
|
211
|
+
- [remote.config.example.ts](remote.config.example.ts): minimal starter config
|
|
212
|
+
- [DESIGN.md](DESIGN.md): architecture and design rationale
|
|
213
|
+
|
|
214
|
+
## Development
|
|
215
|
+
|
|
216
|
+
```sh
|
|
217
|
+
npm run check # typecheck
|
|
218
|
+
npm test # build + test against a mock Core API
|
|
219
|
+
npm run format # prettier
|
|
220
|
+
npm run generate # regenerate wire types from vendor/core-api.json
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Source lives in `src/`. The pinned upstream API specification and its license
|
|
224
|
+
are described in [vendor/README.md](vendor/README.md).
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT, see [LICENSE](LICENSE). The vendored Core API specification is CC-BY-SA-4.0.
|
|
229
|
+
Not affiliated with Unfolded Circle.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CoreClient } from "./client.js";
|
|
2
|
+
import type { ObjectValue, Resource, State, Observation } from "./model.js";
|
|
3
|
+
export declare const ADAPTER = "ucr3-rest-04b0d08-v1";
|
|
4
|
+
export interface Route {
|
|
5
|
+
collection: string;
|
|
6
|
+
item: string;
|
|
7
|
+
collectionTemplate: string;
|
|
8
|
+
itemTemplate: string;
|
|
9
|
+
idField: string;
|
|
10
|
+
singleton?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function route(r: Resource, id?: string): Route;
|
|
13
|
+
export declare function desired(r: Resource): ObjectValue;
|
|
14
|
+
export declare function importShape(r: Resource, value: ObjectValue): ObjectValue;
|
|
15
|
+
export declare class Adapter {
|
|
16
|
+
readonly client: CoreClient;
|
|
17
|
+
constructor(client: CoreClient);
|
|
18
|
+
private included?;
|
|
19
|
+
private includedEntity;
|
|
20
|
+
observe(original: Resource, state: State, key: string): Promise<Observation>;
|
|
21
|
+
validate(r: Resource, create: boolean): void;
|
|
22
|
+
validateCommands(r: Resource): Promise<void>;
|
|
23
|
+
assertDeletable(r: Resource, id: string): Promise<void>;
|
|
24
|
+
upload(r: Resource, update?: boolean): Promise<string>;
|
|
25
|
+
}
|
|
26
|
+
export declare function validateCommandParameters(command: ObjectValue, metadata: ObjectValue, entity: ObjectValue): void;
|