n8n-decanter 0.3.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/CHANGELOG.md +369 -0
- package/LICENSE +21 -0
- package/README.md +318 -0
- package/dist/lib/api.mjs +79 -0
- package/dist/lib/compile.mjs +220 -0
- package/dist/lib/config.mjs +61 -0
- package/dist/lib/diff.mjs +74 -0
- package/dist/lib/executions.mjs +76 -0
- package/dist/lib/git.mjs +44 -0
- package/dist/lib/init.mjs +152 -0
- package/dist/lib/picker.mjs +226 -0
- package/dist/lib/prompt.mjs +34 -0
- package/dist/lib/proxy.mjs +204 -0
- package/dist/lib/pull.mjs +148 -0
- package/dist/lib/push.mjs +136 -0
- package/dist/lib/rename.mjs +156 -0
- package/dist/lib/run.mjs +208 -0
- package/dist/lib/state.mjs +156 -0
- package/dist/lib/status.mjs +102 -0
- package/dist/lib/style.mjs +40 -0
- package/dist/lib/types.mjs +2 -0
- package/dist/lib/util.mjs +201 -0
- package/dist/lib/validate.mjs +225 -0
- package/dist/lib/watch.mjs +296 -0
- package/dist/n8n-decanter.mjs +482 -0
- package/dist/scripts/typecheck.mjs +90 -0
- package/package.json +51 -0
- package/template/.claude/hooks/verify.mjs.example +48 -0
- package/template/.claude/settings.local.json.example +35 -0
- package/template/.cursor/rules/n8n-decanter.mdc.example +21 -0
- package/template/.env.example +10 -0
- package/template/.mcp.json.example +16 -0
- package/template/.vscode/settings.json.example +6 -0
- package/template/AGENTS.md.example +302 -0
- package/template/CLAUDE.md.example +20 -0
- package/template/decanter-ts-plugin/index.js.example +58 -0
- package/template/decanter-ts-plugin/package.json.example +7 -0
- package/template/n8n-globals.d.ts.example +190 -0
- package/template/opencode.json.example +19 -0
- package/template/package.json.example +15 -0
- package/template/shared/.gitkeep +0 -0
- package/template/shared/example-helpers.ts +13 -0
- package/template/tsconfig.json.example +26 -0
- package/template/workflows/.gitkeep +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.3.0] - 2026-07-20
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
|
|
14
|
+
- **Breaking:** `run`'s `$env` no longer exposes the CLI process environment
|
|
15
|
+
by default. Previously a node that read or printed `$env` during `run`
|
|
16
|
+
received every exported variable of the CLI process — including
|
|
17
|
+
`N8N_API_KEY` and any other secret — straight into the JSON on stdout;
|
|
18
|
+
n8n's real `$env` is scoped, this was not. Now `$env` is **empty** unless
|
|
19
|
+
the fixture supplies an `"env"` object (which still wins), and the new
|
|
20
|
+
**`--allow-env`** flag opts back into the old full-inherit behavior for the
|
|
21
|
+
cases that need it (`n8n-decanter <node> run [fixture.json] --allow-env`).
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- The interactive picker's per-workflow verb menu now includes
|
|
26
|
+
**`executions`** (status/pull/push/watch/check/executions), so fetching a
|
|
27
|
+
workflow's real run data no longer requires dropping to the CLI.
|
|
28
|
+
|
|
29
|
+
## [0.2.4] - 2026-07-20
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- `.env.example` and the README now recommend a **scoped** n8n API key —
|
|
34
|
+
limited to the scopes the CLI uses (`workflow:read`/`list`/`update`,
|
|
35
|
+
`execution:read`/`list`) — instead of a full-access key, so a leaked `.env`
|
|
36
|
+
has a smaller blast radius.
|
|
37
|
+
|
|
38
|
+
## [0.2.3] - 2026-07-20
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- **The picker is now a session** — after a verb finishes (or fails: the
|
|
43
|
+
error is logged and you're back in the menu), the picker returns to the
|
|
44
|
+
same workflow's verb menu with the cursor on the verb you just ran, so
|
|
45
|
+
`status` → `pull` needs no re-picking. `Esc` steps back to the workflow
|
|
46
|
+
list (freshly re-scanned, so a just-pulled workflow shows green), `Esc`
|
|
47
|
+
there quits; the exit code reflects the last verb run. The remote
|
|
48
|
+
workflow list is fetched once per session.
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- While the remote workflow list loads, the picker shows light-gray `░`
|
|
53
|
+
placeholder rows of varied widths where the entries will appear, instead
|
|
54
|
+
of a "loading" line.
|
|
55
|
+
- The picker opens with the n8n-decanter logo banner (same as `init`).
|
|
56
|
+
|
|
57
|
+
## [0.2.2] - 2026-07-20
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
- **Interactive workflow picker** — running bare `n8n-decanter` (no verb, no
|
|
62
|
+
arguments) in an inited project on a terminal now opens a picker instead of
|
|
63
|
+
printing usage: type to filter, `↑`/`↓` to move, pulled workflows shown
|
|
64
|
+
green, not-yet-pulled remote ones yellow with a `(not pulled)` marker
|
|
65
|
+
(appended live once the server list loads; skipped without credentials).
|
|
66
|
+
`Enter` on a pulled workflow offers status/pull/push/watch/check (`↑↓` +
|
|
67
|
+
`Enter`, or a letter to cycle matching verbs); `Enter` on an unpulled
|
|
68
|
+
workflow pulls it directly. `Esc` quits, `Ctrl-C` interrupts (exit 130).
|
|
69
|
+
The chosen verb behaves exactly like typing the command. Piped output and
|
|
70
|
+
directories without a `decanter.config.json` keep printing usage — scripts
|
|
71
|
+
and LLM harnesses never see the picker. The `completion zsh|bash` verb
|
|
72
|
+
stays: shell tab completion and the picker cover different moments.
|
|
73
|
+
|
|
74
|
+
## [0.2.1] - 2026-07-19
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- **`executions` verb** — fetches recent execution data (full run JSON,
|
|
79
|
+
newest first) for a workflow into
|
|
80
|
+
`workflows/<Name>/executions/<execId>.json`:
|
|
81
|
+
`n8n-decanter <ref> executions [--status=success|error|waiting]
|
|
82
|
+
[--limit=N]` (default 5, API cap 250; both `--limit=N` and `--limit N`
|
|
83
|
+
work). A numeric argument fetches that single execution by id and routes
|
|
84
|
+
it to its workflow's folder. Read-only against the API. The files show the
|
|
85
|
+
real items each node produced
|
|
86
|
+
(`data.resultData.runData["<Node>"][0].data.main[0][]`) — temporary
|
|
87
|
+
reference data for writing accurate `run` fixtures. Executions run the
|
|
88
|
+
*published* workflow version (n8n 2.x), so they're convenience data, not
|
|
89
|
+
ground truth.
|
|
90
|
+
- **`executions clean`** — offline; deletes fetched `executions/` dirs for
|
|
91
|
+
the given workflow refs, or all pulled workflows without one.
|
|
92
|
+
- Execution data never reaches git: the verb writes each `executions/` dir
|
|
93
|
+
self-ignoring (a `.gitignore` containing `*` — run data can hold
|
|
94
|
+
credentials/PII), and `init`'s scaffolded root `.gitignore` now also
|
|
95
|
+
lists `workflows/*/executions/`.
|
|
96
|
+
- Template `AGENTS.md`: new "Real execution data" section — when to fetch
|
|
97
|
+
executions, where items live in the JSON, copy real shapes into `run`
|
|
98
|
+
fixtures, never commit the data, clean up afterwards.
|
|
99
|
+
|
|
100
|
+
## [0.2.0] - 2026-07-19
|
|
101
|
+
|
|
102
|
+
### Added
|
|
103
|
+
|
|
104
|
+
- The template now ships **`decanter-ts-plugin/`**, a TypeScript
|
|
105
|
+
language-service plugin that stops the editor from flagging legal n8n node
|
|
106
|
+
source — top-level `return`/`await` — with false TS1108/TS1375/TS1378
|
|
107
|
+
errors, while every other diagnostic (and every non-node file) stays live.
|
|
108
|
+
Wired via the sync dir's `tsconfig.json` `plugins` entry and a
|
|
109
|
+
`file:./decanter-ts-plugin` devDependency; `.vscode/settings.json` (new)
|
|
110
|
+
points VS Code at the workspace TypeScript so tsserver can load it — run
|
|
111
|
+
`npm install` and accept *Use Workspace Version* once (JetBrains IDEs use
|
|
112
|
+
the project TypeScript by default). `n8n-decanter check` is unaffected and
|
|
113
|
+
stays authoritative.
|
|
114
|
+
- **Workflow-name arguments**: `pull`/`push`/`status`/`check`/`rename`/`watch`
|
|
115
|
+
now take a workflow's name (or a unique name prefix) wherever they took an
|
|
116
|
+
id — `n8n-decanter "Order Sync" push`. Matching is case-insensitive and
|
|
117
|
+
never prompts: ambiguous or unknown names error with the candidate list.
|
|
118
|
+
`pull` also resolves names of not-yet-pulled workflows against the server's
|
|
119
|
+
workflow list. A workflow literally named like a verb must be addressed by
|
|
120
|
+
id (the verb wins argument detection).
|
|
121
|
+
- `list` verb — one line per pulled workflow (name, id, folder), offline;
|
|
122
|
+
`list --remote` additionally shows remote workflows not pulled yet. The
|
|
123
|
+
discovery surface for what a ref can address.
|
|
124
|
+
- `completion zsh|bash` prints a shell tab-completion script (append to your
|
|
125
|
+
rc file) covering verbs, flags, and local workflow names/ids, backed by a
|
|
126
|
+
hidden credentials-free `__complete` verb.
|
|
127
|
+
- Progress indication: multi-workflow `pull`/`push`/`status` prefix each line
|
|
128
|
+
with a `[2/5]` counter, pull/push result lines get a `(0.4s)` duration
|
|
129
|
+
suffix, and on a terminal a transient `pulling <id>…` line shows while the
|
|
130
|
+
network call runs (piped output only ever gets the result lines).
|
|
131
|
+
- `init` greets with a small ASCII logo + version on a terminal; piped runs
|
|
132
|
+
print a plain `n8n-decanter v<version>` line instead.
|
|
133
|
+
- `watch` prints a deep link straight to the watched workflow's editor page —
|
|
134
|
+
through the live-reload proxy when it is running, the configured n8n host
|
|
135
|
+
otherwise — as a clickable OSC 8 hyperlink on supporting terminals.
|
|
136
|
+
- n8n API requests now **time out after 30 seconds** instead of hanging the
|
|
137
|
+
CLI forever on an unresponsive instance; raise `"requestTimeoutMs"` in
|
|
138
|
+
`decanter.config.json` for slow instances. `init`'s best-effort credential
|
|
139
|
+
probe gives up after 10 seconds.
|
|
140
|
+
- `DEBUG=1` prints the full stack trace when a command fails — the default
|
|
141
|
+
stays the one-line error message.
|
|
142
|
+
- `run` now provides **`$getWorkflowStaticData('global' | 'node')`**, seeded
|
|
143
|
+
from `workflow.json`'s `staticData` (the `global` and the node's own
|
|
144
|
+
`node:` slice) — previously any node using it died with a ReferenceError.
|
|
145
|
+
A fixture `staticData` field (`{ "global": …, "node": … }`) replaces the
|
|
146
|
+
matching slice; mutations are visible during the run but never persisted
|
|
147
|
+
(`run` stays offline). The template's fixture docs cover the new field.
|
|
148
|
+
- **`status --diff`** — prints a unified line diff (`--- remote (n8n)` vs
|
|
149
|
+
`+++ local`) under every drifted node: what a push would change, what a
|
|
150
|
+
pull would bring, or both sides of a CONFLICT. `.ts` nodes diff their
|
|
151
|
+
compiled JS — exactly what the sync hashes compare. In-sync nodes print
|
|
152
|
+
nothing extra.
|
|
153
|
+
- **`.ts` nodes can import now** — shared code from inside the sync dir and
|
|
154
|
+
opted-in npm packages — and push **bundles the imports into the compiled
|
|
155
|
+
node**: the pushed code is self-contained and runs on any instance,
|
|
156
|
+
n8n Cloud included, with no server-side module configuration. Put helpers
|
|
157
|
+
and types in `shared/*.ts` and import them relatively (types *and*
|
|
158
|
+
values); npm packages bundle after a normal install in the sync dir plus a
|
|
159
|
+
`"bundleDependencies": ["zod", …]` opt-in in `decanter.config.json`
|
|
160
|
+
(pure-JS packages only). Rules, enforced by `check` and the compiler:
|
|
161
|
+
imports at the top of the file, relative imports stay inside the sync dir,
|
|
162
|
+
Node builtins and unlisted packages are errors. Nodes without imports
|
|
163
|
+
compile byte-identically to before — no drift noise on upgrade.
|
|
164
|
+
Previously *any* import — even `import type` — failed the push compile
|
|
165
|
+
outright ("Top-level return cannot be used inside an ECMAScript module").
|
|
166
|
+
Editing a shared file marks every importing node push-pending in `status`
|
|
167
|
+
(`--diff` shows the inlined change); pushing propagates it. Oversized
|
|
168
|
+
compiles (> 100 KB) warn. The template ships `shared/example-helpers.ts`
|
|
169
|
+
and updated agent guidance.
|
|
170
|
+
|
|
171
|
+
### Changed
|
|
172
|
+
|
|
173
|
+
- `workflow.json` stays lean on n8n 2.x: `pull` now keeps the file to the
|
|
174
|
+
workflow itself — the server-side copy of the published version
|
|
175
|
+
(`activeVersion`, which duplicates every node's code) and sharing metadata
|
|
176
|
+
(`shared`) are left out. Your code exists exactly once (in `code/`), and
|
|
177
|
+
git diffs show your edits instead of publish churn. Nothing is lost:
|
|
178
|
+
neither field can be pushed anyway.
|
|
179
|
+
- **Breaking:** `status` now exits **1 when a pull is needed or a push would
|
|
180
|
+
clobber remote work** — on a CONFLICT, remote-only changes (structure or
|
|
181
|
+
node code), remote code nodes unknown locally, remotely deleted nodes, or a
|
|
182
|
+
workflow not pulled yet. Local-only "push pending" edits still exit 0.
|
|
183
|
+
Scripts that relied on `status` always exiting 0 must check output instead.
|
|
184
|
+
|
|
185
|
+
- CLI output is styled — color, `✓`/`!`/`✗` glyphs, bold names, dim
|
|
186
|
+
metadata — **only when the stream is a terminal**, honoring `NO_COLOR` and
|
|
187
|
+
`FORCE_COLOR`; piped/redirected output stays plain line-oriented text (no
|
|
188
|
+
information is carried by color alone). Error lines now start with `✗ `
|
|
189
|
+
(was `x `), success lines with `✓ `.
|
|
190
|
+
|
|
191
|
+
### Fixed
|
|
192
|
+
|
|
193
|
+
- ANSI escape codes no longer leak into piped output — previously the two
|
|
194
|
+
hardcoded warn/error colors were emitted unconditionally, polluting logs,
|
|
195
|
+
scripts, and LLM harness transcripts.
|
|
196
|
+
- `init` from the npm-installed package no longer fails to find `template/`:
|
|
197
|
+
it resolved the directory relative to the compiled `dist/lib/`, a location
|
|
198
|
+
that exists in a git checkout but not in the published tarball. The
|
|
199
|
+
template (and the version banner) now resolve via the nearest
|
|
200
|
+
`package.json`, which works in both layouts.
|
|
201
|
+
- The compliance guard now rejects a `.js` node containing an `import` —
|
|
202
|
+
`.js` nodes are pushed verbatim, so the import would reach n8n unbundled
|
|
203
|
+
and fail at runtime; the error points to `.ts` (where imports are bundled)
|
|
204
|
+
or inlining.
|
|
205
|
+
|
|
206
|
+
## [0.1.0] - 2026-07-18
|
|
207
|
+
|
|
208
|
+
First public release.
|
|
209
|
+
|
|
210
|
+
### Added
|
|
211
|
+
|
|
212
|
+
- Push, watch, and `status` now report the workflow's **publication state**
|
|
213
|
+
(n8n 2.x draft/publish model): push result lines end in
|
|
214
|
+
`— published: code is live now` or `— unpublished: draft only`, `watch`
|
|
215
|
+
warns at start when the workflow is published (n8n auto-publishes every
|
|
216
|
+
API update to a published workflow — there is no draft-only push), and
|
|
217
|
+
`status` shows `published`/`unpublished` in its header line. Servers that
|
|
218
|
+
don't report an `active` flag are unaffected.
|
|
219
|
+
- `watch` now also watches **`workflow.json`** and pushes structural edits
|
|
220
|
+
(connections, node settings, …) on save — the IDE becomes a peer editor of
|
|
221
|
+
the n8n UI. A save only pushes cleanly when the remote structure is
|
|
222
|
+
unchanged since the last sync; if both sides changed, an interactive
|
|
223
|
+
prompt offers **[m]erge** (writes a diff-friendly `workflow.remote.json`
|
|
224
|
+
to reconcile manually), **[l]ocal** (force-push over the remote changes),
|
|
225
|
+
**[r]emote** (pull over the local file; the previous version stays in
|
|
226
|
+
git), or Enter to skip. Non-interactive sessions log the conflict and
|
|
227
|
+
skip; `--force` resolves as keep-local without asking. n8n-UI structural
|
|
228
|
+
edits detected after a node push produce an early warning. `check` warns
|
|
229
|
+
while an unreconciled `workflow.remote.json` exists.
|
|
230
|
+
|
|
231
|
+
### Changed
|
|
232
|
+
|
|
233
|
+
- `watch` starts every session with a **safety commit + pull** of the
|
|
234
|
+
workflow folder: local state is committed first (even with
|
|
235
|
+
`commitOnPush`/`commitOnPull` off — it's the data-loss guard, skipped on a
|
|
236
|
+
clean tree), then the workflow is pulled so watch begins from a committed,
|
|
237
|
+
in-sync baseline. Without git, the startup pull is skipped with a warning
|
|
238
|
+
instead of risking uncommitted edits.
|
|
239
|
+
- `watch` no longer refuses workflows without Code nodes — they are
|
|
240
|
+
watchable for structural (`workflow.json`) changes.
|
|
241
|
+
|
|
242
|
+
### Fixed
|
|
243
|
+
|
|
244
|
+
- One corrupt `.decanter.json` no longer breaks every command for every
|
|
245
|
+
workflow: `pull`/`push`/`status`/`watch` now skip the broken folder with a
|
|
246
|
+
warning, and `check` (and the push gate) report a scoped
|
|
247
|
+
"corrupt .decanter.json (…)" compliance error for that folder — previously
|
|
248
|
+
a raw `SyntaxError` aborted the whole command, healthy workflows included.
|
|
249
|
+
- Malformed `decanter.config.json`, and malformed `workflow.json` in
|
|
250
|
+
`rename`, now fail with an error naming the offending file instead of
|
|
251
|
+
leaking a bare JSON `SyntaxError`.
|
|
252
|
+
- `watch`: pushing a node whose `.decanter.json` entry disappeared
|
|
253
|
+
mid-session (e.g. removed by a concurrent pull) now fails with a clear
|
|
254
|
+
"pull first" error instead of a `TypeError`.
|
|
255
|
+
|
|
256
|
+
### Changed
|
|
257
|
+
|
|
258
|
+
- **Breaking:** `watch` now takes a **workflow id** and watches every Code
|
|
259
|
+
node in that workflow's `code/` dir, pushing whichever node you save
|
|
260
|
+
(previously it took a single node file and watched only that one). Run
|
|
261
|
+
`n8n-decanter <id> watch`, or omit the id when `decanter.config.json` lists
|
|
262
|
+
exactly one workflow. This matches the new browser live-reload, which is
|
|
263
|
+
workflow-scoped.
|
|
264
|
+
- The compliance guard (`check`, the push gate, watch) now also enforces
|
|
265
|
+
structural integrity: dangling connection sources/targets, duplicate node
|
|
266
|
+
names or ids, orphan `.js`/`.ts` files no `//@file:` placeholder references
|
|
267
|
+
(`.d.ts`, `.remote.js`, and subdirs other than `code/` are exempt), and
|
|
268
|
+
dangling literal `$('…')` references in node source files and expression
|
|
269
|
+
parameters are all errors now. These checks may flag pre-existing issues
|
|
270
|
+
in already-pulled workflows — that's the point; fix them or the push stays
|
|
271
|
+
blocked (`--force` does not bypass the guard).
|
|
272
|
+
- **Breaking:** node sources now live in a `code/` subdir inside each
|
|
273
|
+
workflow folder, named in kebab-case after their node (`Parse Order` →
|
|
274
|
+
`code/parse-order.js`). `//@file:` placeholders and `.decanter.json`
|
|
275
|
+
entries carry the `code/` prefix, `.remote.js` conflict artifacts land in
|
|
276
|
+
`code/` too, and `check`/`push` reject node files outside it. Existing
|
|
277
|
+
folders migrate automatically on the next `pull` (files are renamed in
|
|
278
|
+
place).
|
|
279
|
+
- `check <id …>` with explicit workflow ids now scopes the typecheck too:
|
|
280
|
+
only diagnostics from the given workflows' folders are reported and
|
|
281
|
+
counted (the whole project still compiles, so cross-file types keep
|
|
282
|
+
working). Bare `check` stays project-wide.
|
|
283
|
+
- Template: the PostToolUse verify hook scopes its check to the edited
|
|
284
|
+
workflow (it reads the workflow id from the sibling `.decanter.json`), so
|
|
285
|
+
errors in unrelated workflows no longer block an edit.
|
|
286
|
+
- Template: node files are typechecked as separate module scopes
|
|
287
|
+
(`moduleDetection: "force"` in `tsconfig.json`) — same-named top-level
|
|
288
|
+
declarations in different node files no longer raise false "cannot
|
|
289
|
+
redeclare" errors.
|
|
290
|
+
- **Breaking:** requires Node >= 22.18 (was >= 18.17). The CLI is now
|
|
291
|
+
written in TypeScript and executed natively via Node's type stripping —
|
|
292
|
+
no build step. The entry point is `n8n-decanter.mts` (invoke as
|
|
293
|
+
`node n8n-decanter.mts …`); the installed `n8n-decanter` bin name is
|
|
294
|
+
unchanged.
|
|
295
|
+
- Template: the Claude Code permission examples
|
|
296
|
+
(`.claude/settings.local.json`) now reference the `n8n-decanter.mts`
|
|
297
|
+
entry point.
|
|
298
|
+
|
|
299
|
+
### Added
|
|
300
|
+
|
|
301
|
+
- Browser live-reload for `watch` (opt-in). Set `"browserReload": "proxy"` in
|
|
302
|
+
`decanter.config.json` and `watch` boots a transparent reverse proxy on
|
|
303
|
+
`127.0.0.1:5679` (override with `"proxyPort"`) that forwards everything to
|
|
304
|
+
your n8n host — auth, assets, and n8n's native `/rest/push` WebSocket — while
|
|
305
|
+
injecting a small live-reload client into the editor HTML. Open the editor
|
|
306
|
+
through the proxy URL; each successful single-node push then refreshes the
|
|
307
|
+
tab automatically, **unless the editor has unsaved changes** — then it logs a
|
|
308
|
+
console warning and leaves your in-browser work untouched. If the port can't
|
|
309
|
+
be bound, `watch` warns and keeps syncing without live reload. Works cleanly
|
|
310
|
+
against a local http n8n; https/remote upstreams are best-effort (Secure
|
|
311
|
+
cookies don't survive the plain-http hop). Default off.
|
|
312
|
+
- `rename` verb: `n8n-decanter rename <id> "<old node>" "<new node>"` renames
|
|
313
|
+
a node atomically everywhere the old name is load-bearing — `node.name`,
|
|
314
|
+
connection keys and targets, literal `$('…')` references in every node
|
|
315
|
+
source file and expression parameter, the kebab-case source filename (plus
|
|
316
|
+
its `.remote.js` sibling), the `//@file:` placeholder, and the
|
|
317
|
+
`.decanter.json` entry. Refuses names that already exist; validates the
|
|
318
|
+
result and fails loudly if anything is left dangling. Offline — `push`
|
|
319
|
+
propagates. `rename <id> --workflow "<new name>"` renames the workflow
|
|
320
|
+
itself (the folder follows on the next pull).
|
|
321
|
+
- Id-first argument order: `n8n-decanter.mts wf123 push` ==
|
|
322
|
+
`n8n-decanter.mts push wf123` — the first token matching a known verb is
|
|
323
|
+
taken as the command; everything else, including flags, may appear in any
|
|
324
|
+
position. The CLI help and README document id-first as the canonical form.
|
|
325
|
+
- Template: the `n8n-globals.d.ts` stub declares Luxon `Duration` and
|
|
326
|
+
`Interval` (pragmatic subsets, matching the existing `DateTime` stub) —
|
|
327
|
+
both were already advertised in `AGENTS.md` and provided at runtime, only
|
|
328
|
+
the type stubs were missing. The AGENTS notes now also call out the
|
|
329
|
+
editor-only TS1108 top-level-`return` squiggle as a false positive.
|
|
330
|
+
- `init --force` — re-copies template files over existing ones in the
|
|
331
|
+
target (`.env` is always protected); every overwrite is logged.
|
|
332
|
+
- Commit-on-sync: after every successful `push` (including `watch`'s
|
|
333
|
+
single-node pushes) and every successful `pull`, the workflow's folder is
|
|
334
|
+
git-committed, pathspec-scoped so unrelated staged changes stay untouched;
|
|
335
|
+
no empty commits; a pull that renames the folder commits the old path's
|
|
336
|
+
deletions too. Disable with `"commitOnPush": false` / `"commitOnPull":
|
|
337
|
+
false` in `decanter.config.json` (default: on). Outside a git repo it
|
|
338
|
+
warns and continues.
|
|
339
|
+
|
|
340
|
+
- `pull` — extracts each Code node's `jsCode` into its own `<Node>.js` file
|
|
341
|
+
(lossless, byte-identical round-trip) behind a `//@file:` placeholder in
|
|
342
|
+
`workflow.json`; tracks state in per-folder `.decanter.json`; follows
|
|
343
|
+
workflow/node renames by id; surfaces unmergeable remote changes as
|
|
344
|
+
`<Node>.remote.js` instead of touching local sources.
|
|
345
|
+
- `push` — reassembles workflows and PUTs them (whitelisted fields only);
|
|
346
|
+
`.ts` nodes compile one-way via esbuild and carry a
|
|
347
|
+
`// @ts-n8n sha256:…` marker; drift guard aborts when the remote changed
|
|
348
|
+
since the last sync (`--force` overrides only this).
|
|
349
|
+
- Compliance guard + `check` command — blocks pushes that violate the
|
|
350
|
+
layout (inline code in `workflow.json`, missing/`.remote.js`/non-`.js`/`.ts`
|
|
351
|
+
file references, `@ts-n8n` marker inside a `.js` node); not bypassable with
|
|
352
|
+
`--force`; `check` also runs standalone and offline (no credentials).
|
|
353
|
+
- Typecheck gate on push (`--no-typecheck` to skip) via
|
|
354
|
+
`scripts/typecheck.mjs`, which wraps node-file function bodies in memory so
|
|
355
|
+
`tsc` accepts their top-level `return`/`await`.
|
|
356
|
+
- `watch <node-file>` — pushes a single node on every save (debounced,
|
|
357
|
+
atomic-save-proof directory watch).
|
|
358
|
+
- `init [dir]` — interactive bootstrap: prompts for host/API key (piped
|
|
359
|
+
stdin works too; skipped entirely when `.env` already holds both values),
|
|
360
|
+
writes `.env`, copies `template/` completely with
|
|
361
|
+
`X.example` files materializing as `X`, scaffolds `decanter.config.json`
|
|
362
|
+
and `.gitignore`, best-effort credential check.
|
|
363
|
+
- `status` — per-node and structural local-vs-remote drift report.
|
|
364
|
+
- Template starter kit for init'ed dirs: `AGENTS.md`/`CLAUDE.md`, Claude
|
|
365
|
+
Code permission settings, `opencode.json` permissions, Cursor rule,
|
|
366
|
+
`.mcp.json` embedding [n8n-mcp](https://github.com/czlonkowski/n8n-mcp)
|
|
367
|
+
through an `.env`-sourcing wrapper, and a `shared/` dir for shared types.
|
|
368
|
+
- `n8n-globals.d.ts` ambient types for Code nodes; e2e suite against a mock
|
|
369
|
+
n8n API (`npm test`).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Malte Buttjer
|
|
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.
|