]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/log
e-mobility-charging-stations-simulator.git
2 days agofix(ui-server): harden CSMS credential handling in supervision URL flow
Jérôme Benoit [Wed, 22 Apr 2026 18:54:58 +0000 (20:54 +0200)] 
fix(ui-server): harden CSMS credential handling in supervision URL flow

- Add autocomplete="off" on supervision password inputs
- Reject colon in supervisionUser via Zod regex (RFC 7617)
- Strip credentials from broadcast channel error response payloads

2 days agofix(ui-server): harmonize setSupervisionUrl semantics and text descriptions
Jérôme Benoit [Wed, 22 Apr 2026 18:28:07 +0000 (20:28 +0200)] 
fix(ui-server): harmonize setSupervisionUrl semantics and text descriptions

- Fix SetSupervisionUrl.vue passing empty credentials that silently
  clear existing values; align with AddChargingStations.vue pattern
- Fix double saveStationInfo() call and missing updated event when
  only URL changes in ChargingStation.setSupervisionUrl
- Remove verbose JSDoc, redundant comment, and parenthetical UI label
- Harmonize MCP descriptions: use natural language instead of camelCase
  code names, unify credential field descriptions across schemas

2 days agofeat(ui-server): allow override of station identity and CSMS credentials in addChargi...
Daniel [Wed, 22 Apr 2026 17:50:28 +0000 (19:50 +0200)] 
feat(ui-server): allow override of station identity and CSMS credentials in addChargingStations (#1802)

* feat(ui-server): allow per-call override of station identity and CSMS credentials

Extend ChargingStationOptions with baseName, fixedName, nameSuffix,
supervisionUser and supervisionPassword so that callers of the
addChargingStations UI procedure can fully customise a station without
editing template files.

Identity derivation (chargingStationId, hashId) moves to the tail of
getStationInfo, after setChargingStationOptions runs, so the merged
stationInfo is the single source of truth for baseName/fixedName/nameSuffix.
getChargingStationId now accepts a narrow StationIdentity pick that both
ChargingStationTemplate and ChargingStationInfo satisfy; getHashId grows
an optional chargingStationIdOverride so the new identity flows through
without altering the hashed payload shape for existing callers.

Default behaviour is unchanged: hash ID regression test pins the
pre-existing hash, and omitted options leave stationInfo untouched.

* docs(ui-server): sync ChargingStationOptions docs with source interface

Bring the README protocol reference, the MCP zod schema and the web UI
ChargingStationOptions type back in sync with src/types/ChargingStationWorker.ts.
Covers the newly added baseName, fixedName, nameSuffix, supervisionUser and
supervisionPassword fields, plus previously missing fields not yet surfaced
in the public docs.

* feat(ui-web): expose baseName, fixedName and CSMS credentials in add-station dialog

Surface the new addChargingStations options on the web UI form: a base
name text field, an "append counter to name" checkbox that controls
fixedName, and a supervision user/password pair alongside the existing
supervision URL. Empty fields are omitted from the payload so the
template defaults apply unchanged.

\ 1 Conflicts:
\ 1 ui/web/src/components/actions/AddChargingStations.vue

* feat(ui-server): allow setSupervisionUrl to update CSMS credentials

Extend the setSupervisionUrl UI procedure so callers can update the
supervision WebSocket's basic auth user and password on an already
running station, not only the URL. All three fields (url,
supervisionUser, supervisionPassword) become optional; the handler now
requires at least one to be set.

ChargingStation.setSupervisionUrl accepts the three optional arguments
and writes the non-URL fields onto stationInfo via saveStationInfo.
Changes take effect on the next WebSocket (re)connect, mirroring how
URL updates already worked. The MCP zod schema and the protocol
reference in the README are updated accordingly.

* feat(ui-web): expose CSMS credentials in set-supervision dialog

Surface the extended setSupervisionUrl procedure on the web UI: user
and password inputs next to the existing URL field, a client-side
guard that requires at least one field set, and a renamed form
header ("Set Supervision") that matches the broader scope. UIClient
passes the new fields through only when populated, so existing
URL-only callers are unaffected.

* fix(ui-web): serve index.html as SPA fallback so deep-linked routes survive reload

The built bundle is served by start.js via serve-static, which 404s on
any path that isn't a physical file — so refreshing a router route
like /add-charging-stations or /set-supervision-url/... produced
"Cannot GET ...". When serve-static gives up, fall back to sending
the bundled index.html with 200 OK so the SPA router can resolve the
route client-side. POST/PUT/etc. still fall through to finalhandler.

* [autofix.ci] apply automated fixes

* test(ui-web): update AddChargingStations tests for new fields

Bump checkbox count to 5 (appendCounter added) and cover baseName,
supervision credential pass-through and fixedName behaviour.

* fix: PR comments

* fix: PR comments

  - server: setSupervisionUrl: url required, supervisionUser/supervisionPassword
    independent (string updates incl. "" to clear, undefined preserves);
    drop block-comment noise; align README and MCP schema descriptions
  - ui: SetSupervisionUrl dialog: always send credentials verbatim — empty
    fields clear stored credentials (frontend can't distinguish null
    vs empty)
  - ui: AddChargingStations dialog: replace inverted appendCounter with
    fixedName mirroring the API field; label "(base name is full
    station name)"; empty fields keep preserving template defaults
  - server: Rename StationIdentity → ChargingStationNameTemplate (and the
    parameter in getChargingStationId) so the type name reflects its
    role as the building blocks of chargingStationId
  - Tests updated to match the new semantics

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
3 days agofix(ui-server): reject empty password in basic auth token validation (RFC 7613 §4.1)
Jérôme Benoit [Wed, 22 Apr 2026 15:07:41 +0000 (17:07 +0200)] 
fix(ui-server): reject empty password in basic auth token validation (RFC 7613 §4.1)

Align UIServer basic auth with RFC 7617/7613: empty passwords are now
rejected alongside the existing empty username check. Add comprehensive
unit tests for all UIServerUtils exported functions (24 tests).

3 days agofix(deps): update all non-major dependencies (#1807)
renovate[bot] [Wed, 22 Apr 2026 09:21:37 +0000 (11:21 +0200)] 
fix(deps): update all non-major dependencies (#1807)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
3 days agofix(cli): make high-level OCPP commands version-aware (#1801)
Jérôme Benoit [Wed, 22 Apr 2026 09:06:51 +0000 (11:06 +0200)] 
fix(cli): make high-level OCPP commands version-aware (#1801)

* fix(cli): make high-level transaction and authorize commands OCPP version-aware

- transaction start: detect station OCPP version, send TRANSACTION_EVENT
  with idToken/evse payload for 2.0.x or START_TRANSACTION for 1.6
- transaction stop: same version detection, send TRANSACTION_EVENT Ended
  for 2.0.x or STOP_TRANSACTION for 1.6; accept string transaction IDs
- ocpp authorize --id-tag: send idToken object for 2.0.x, idTag for 1.6;
  -p payload passthrough unchanged (low-level override)
- action.ts: extract fetchStationList helper, export resolveOcppVersion

* docs(cli): clarify prefix matching behavior in resolveOcppVersion JSDoc

* refactor(cli): use switch/case for OCPP version branching and clean up help text

* fix(cli): address audit findings for cross-version OCPP compliance

- R1: status-notification version-aware: --error-code optional, validated
  for OCPP 1.6; OCPP 2.0.x uses connectorStatus with optional --evse-id
- R2: add optional --evse-id to meter-values
- R3: skipped (callers already throw clear error for heterogeneous/no-match)
- R4: skipped (handleStopTransaction is 1.6-only, transactionId always number)
- R5: JSDoc on version-specific ProcedureName entries
- R6: comment on handleStopTransaction noting 1.6-only

* refactor(cli): consolidate version detection, fix DRY violations, and harden edge cases

- F-01: comment noting OCPP20TriggerReasonEnumType duplication
- F-02/F-04: extract resolveOcppVersionFromProgram (single fetch, returns resolved hashIds)
- F-03: export MIXED_OCPP_VERSION_ERROR constant
- F-05: default evse.id to 1, add --evse-id to transaction start
- F-06: document -p limitation in help text
- F-07: remove idTag ?? '' dead code
- F-08: remove redundant === id check
- F-09: add test for ambiguous prefix with homogeneous versions
- F-10: rewrite handleStopTransaction comment

* style: remove unnecessary duplication comments in ui/common enums

* refactor(cli): use commander conflicts() for mutually exclusive options

ocpp authorize: --id-tag and -p/--payload are now declared as mutually
exclusive via Option.conflicts(). Commander enforces this at parse time.

* fix(cli): resolve audit findings — BUG-01, DRY-01, DEAD-01, ROBUST-01, PERF-01

- BUG-01: use resolvedHashIds (not raw hashIds) in resolveOcppVersionFromProgram
- DRY-01: extract resolveShortHashIdsFromList pure helper, used by both
  resolveShortHashIds and resolveOcppVersionFromProgram
- DEAD-01/DOC-01: remove unused resolveOcppVersion, update JSDoc references
- ROBUST-01: seqNo 1 for transaction stop (start uses 0)
- PERF-01: resolveOcppVersionFromProgram returns config, runAction accepts
  preloadedConfig to skip redundant loadConfig call

* chore: refine .gitignore

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
* fix(cli): simplify OCPP 2.0.x transaction payloads to match builder options

- transaction start: send flat OCPP20TransactionEventOptions (connectorId,
  evseId, idToken) instead of wire-format fields (seqNo, timestamp,
  transactionInfo nesting, evse structure)
- transaction stop: send flat transactionId instead of nested
  transactionInfo, remove hardcoded seqNo/timestamp/triggerReason
- Remove evseId ?? 1 default (let builder resolve from connectorId)
- Remove unused imports (randomUUID, OCPP20TriggerReasonEnumType)
- Fix meter-values --evse-id misleading help text
- Add test for mixed matching/non-matching hashIds

Addresses review comments from hyperspace-insights and Copilot,
cross-validated with audit v2 findings BUG-01, BUG-02, BUG-03.

* fix(cli): harmonize all OCPP commands with version-aware conflicts pattern

- Add early NO_STATIONS_ERROR check in resolveOcppVersionFromProgram
- Refactor status-notification, meter-values, transaction start/stop to
  use .addOption/.conflicts() pattern matching authorize's design
- Make meter-values version-aware: OCPP 1.6 sends connectorId, OCPP 2.0
  sends evseId, passthrough mode sends only routing fields
- Fix --evse-id help text in transaction start (was claiming false default)
- Fix pre-existing jsdoc/require-throws-type lint warning

* fix(cli): add connectorId to transaction stop for OCPP 2.0.x and harmonize evseId conflicts

- Add --connector-id option to transaction stop (defaults to 1) so
  buildTransactionEvent resolves the correct connector on multi-connector
  stations
- Convert all --evse-id options to .addOption/.conflicts('payload') for
  consistent UX with other semantic options
- Add evseId to reverse .conflicts() lists on -p/--payload options

* fix(cli): update JSDoc and comments to match current behavior

- Update resolveOcppVersionFromProgram JSDoc: returns OCPPVersion (not
  undefined), add @throws documentation for all error cases
- Update MIN_FULL_HASH_LENGTH comment: now validates existence, not just
  skips resolution

* fix(cli): route version-detection errors through formatter for --json mode

Add handleActionErrors wrapper that catches errors from version
detection, option validation, and hash-ID resolution before runAction,
routing them through the same formatter used by runAction. Without this,
errors in --json mode were output as plain text instead of structured
JSON.

* Apply suggestion from @jerome-benoit

* refactor(cli): extract shared error formatter and clean up exports

- Extract formatError() from duplicated catch blocks in
  handleActionErrors and runAction
- Align authorize error message with the pattern used by all other
  commands ('X is required when -p/--payload is not provided')
- Remove unnecessary exports from 3 internal-only error constants
  (NO_STATIONS_ERROR, MIXED_OCPP_VERSION_ERROR, UNKNOWN_OCPP_VERSION_ERROR)

* fix(cli): standardize option help texts and remove redundancy

- Standardize EVSE ID help text to 'EVSE ID (OCPP 2.0.x; derived from
  connector ID if omitted)' across meter-values, status-notification,
  and transaction start
- Simplify error-code help text from 'OCPP 1.6 only; required for 1.6'
  to 'OCPP 1.6' (the constraint is already enforced at runtime)

* docs(cli): document version-aware commands and new OCPP 2.0.x options

- Add --evse-id to meter-values, status-notification, transaction start
- Add --connector-id to transaction stop (required for OCPP 2.0.x)
- Mark --error-code as optional (OCPP 1.6 only)
- Mark --vendor-id as optional in data-transfer
- Document -p/--payload support for transaction commands
- Add Version-aware commands section with OCPP 1.6 vs 2.0.x mapping
- Update both README.md and SKILL.md consistently

* docs(cli): fix pre-existing documentation gaps in README and SKILL.md

- Add missing station add options: --persistent-config, --ocpp-strict
- Add missing station delete --delete-config example in SKILL.md
- Add missing atg stop --connector-ids in SKILL.md
- Fix meter-values: both --connector-id and --evse-id are optional
  (at least one required)
- Fix ATG --connector-ids notation: <id,...> instead of <ids...>
- Add hashId prefix matching note to SKILL.md

* fix(cli): harmonize user-facing strings and fix documentation accuracy

Code:
- Harmonize 'station(s)' terminology (drop 'charging' prefix in
  station.ts descriptions to match all other command files)
- Fix NO_STATIONS_ERROR to use consistent 'stations' wording
- Fix data-transfer --data description: free-form string, not JSON only
- Fix transaction unsupported version error: direct users to
  'ocpp transaction-event -p' instead of generic '-p' which forces 1.6

Docs (README + SKILL.md):
- Fix merge claim: -p conflicts with typed options on most commands,
  only data-transfer allows merge
- Add meter-values 'at least one required' constraint note
- Add --connector-id required for transaction stop on OCPP 2.0.x in
  version-aware table
- Fix data-transfer --data placeholder from <json> to <data>

* refactor(cli): extract duplicated unsupported version error in transaction.ts

Extract inline error string into a local const within
createTransactionCommands to avoid duplication across the start and
stop default switch branches.

* fix(cli): correct meter-values payload for OCPP 2.0.x stations

The OCPP 2.0.x MeterValues JSON schema does not allow connectorId as a
property. The previous implementation conditionally included connectorId
in the payload for OCPP 2.0.x, which caused schema validation failures
in the simulator ("additionalProperty: connectorId — must NOT have
additional properties").

For OCPP 2.0.x, evseId is the only valid EVSE identifier in a
MeterValues request. The fix now requires --evse-id for OCPP 2.0.x
stations and sends only evseId in the payload. If only --connector-id
is supplied against a 2.0.x station, a clear error message guides the
user to use --evse-id instead. The --connector-id help text is also
updated to indicate it is OCPP 1.6 only for this command.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(cli): align meter-values documentation with evseId-only OCPP 2.0.x fix

Update README and SKILL.md to reflect that meter-values requires
--evse-id for OCPP 2.0.x (connectorId is not a valid MeterValues
field in 2.0.x). Show separate OCPP 1.6 and 2.0.x usage lines.

* fix(cli): harmonize meter-values help texts and error message

- Drop 'only' from --connector-id help: '(OCPP 1.6)' matches
  --error-code pattern in status-notification
- Drop 'required' from --evse-id help: '(OCPP 2.0.x)' matches
  version-only pattern used for version-exclusive options
- Remove verbose parenthetical from evseId error message to match
  the terse pattern used by all other version-specific errors

* fix(cli): use SCREAMING_CASE for error constant and add missing exit code

- Rename unsupportedVersionError to UNSUPPORTED_VERSION_ERROR in
  transaction.ts to match the naming convention used by all error
  constants in action.ts
- Add exit code 143 (SIGTERM) to SKILL.md exit codes table

* fix(cli): complete resolveOcppVersionFromList JSDoc for unknown versions

Add 'or all versions are unknown' to the @returns description to
document that the function also returns undefined when all targeted
stations have ocppVersion: undefined (Set {undefined}, size 1).

* fix(cli): align error constant naming and fix README station list comment

- Rename UNSUPPORTED_VERSION_ERROR to UNSUPPORTED_OCPP_VERSION_ERROR in
  transaction.ts to match the naming convention with OCPP_ prefix used
  by the exported constant in action.ts
- Fix README inline comment: 'List all stations' to match station.ts
  description (was still 'List all charging stations')

---------

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
4 days agofix(webui-docker): include workspace root node_modules so pnpm symlinks resolve ...
Daniel [Tue, 21 Apr 2026 11:52:00 +0000 (13:52 +0200)] 
fix(webui-docker): include workspace root node_modules so pnpm symlinks resolve (#1806)

ui/web/node_modules is a pnpm workspace package whose entries are
symlinks into ../../../node_modules/.pnpm/..., i.e. the workspace
root's store. The previous Dockerfile only copied ui/web/node_modules
into the final image, leaving every symlink dangling and breaking
`node start.js` with "Cannot find package 'finalhandler'".

Copy the workspace root node_modules to /node_modules in the final
image so the relative symlinks resolve.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 days agofix(deps): update all non-major dependencies (#1803)
renovate[bot] [Tue, 21 Apr 2026 10:24:25 +0000 (12:24 +0200)] 
fix(deps): update all non-major dependencies (#1803)

* fix(deps): update all non-major dependencies

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
4 days agochore(deps): update all non-major dependencies (#1799)
renovate[bot] [Mon, 20 Apr 2026 16:56:19 +0000 (18:56 +0200)] 
chore(deps): update all non-major dependencies (#1799)

* chore(deps): update all non-major dependencies

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
5 days agorefactor: remove redundant EmptyObject from CommandResponse union
Jérôme Benoit [Mon, 20 Apr 2026 13:46:06 +0000 (15:46 +0200)] 
refactor: remove redundant EmptyObject from CommandResponse union

EmptyObject (Record<string, never>) is structurally assignable to every
object type in the union. Empty responses are handled at runtime via
isEmpty() checks before reaching type-narrowing logic.

5 days agodocs: update openspec config with UI Common and CLI sub-projects
Jérôme Benoit [Sun, 19 Apr 2026 16:28:28 +0000 (18:28 +0200)] 
docs: update openspec config with UI Common and CLI sub-projects

Fix outdated monorepo structure (3 → 5 sub-projects), tech stack,
architecture tree, and quality gates in openspec/config.yaml.

5 days agodocs: add monorepo structure to copilot instructions, clarify command scopes
Jérôme Benoit [Sun, 19 Apr 2026 16:19:25 +0000 (18:19 +0200)] 
docs: add monorepo structure to copilot instructions, clarify command scopes

- Add monorepo structure section to copilot-instructions.md
- Simplify quality gates to reference sub-project READMEs (no duplication)
- Clarify opencode format/test commands scope to root simulator only
- Add pnpm build to ui/common README scripts table
- Fix duplicate bullet points and table alignment

6 days agodocs: harmonize project memories with current codebase state
Jérôme Benoit [Sun, 19 Apr 2026 15:12:31 +0000 (17:12 +0200)] 
docs: harmonize project memories with current codebase state

- Add UI Common and CLI sections to all 4 memories
- Fix UI Common build = typecheck (source-only package, no artifacts)
- Fix monorepo description: 4 TS packages (pnpm) + 1 Python (Poetry)
- Fix OCPP Server coverage threshold column placement
- Flag Vitest watch mode in commands and checklist
- Add Zod dependency and config validation convention
- Fix source tree comment (3 transports, not 2)
- Update CI matrix to Python 3.13/3.14
- Add CLI conventions (factory pattern, payload helpers, short hash, output)
- Add UI Common conventions (canonical types, no re-exports, errors)
- Add per-framework testing conventions (root strict vs ui assert vs vitest)
- Refactor checklist to remove command duplication with suggested_commands
- Fix README references to actual project files

7 days agoci: update mypy target to Python 3.14 in OCPP mock server
Jérôme Benoit [Fri, 17 Apr 2026 23:23:18 +0000 (01:23 +0200)] 
ci: update mypy target to Python 3.14 in OCPP mock server

7 days agoci: update Python matrix from 3.12/3.13 to 3.13/3.14
Jérôme Benoit [Fri, 17 Apr 2026 23:18:27 +0000 (01:18 +0200)] 
ci: update Python matrix from 3.12/3.13 to 3.13/3.14

Python 3.12 is in security-only mode (no more bugfixes).
Python 3.14 is stable with active bugfix support (3.14.3 released).
Run typecheck, lint, and coverage on 3.14 (latest stable).

7 days agofeat(ui-cli): add custom JSON payload option for OCPP and transaction commands
Jérôme Benoit [Fri, 17 Apr 2026 23:10:32 +0000 (01:10 +0200)] 
feat(ui-cli): add custom JSON payload option for OCPP and transaction commands

Add -p, --payload <json|@file|-> to all 16 OCPP commands and both
transaction commands. Supports three input modes:
- Inline JSON: -p '{"reason":"PowerUp"}'
- File: -p @collections/boot.json
- Stdin: echo '{}' | evse-cli ocpp heartbeat -p -

Payload is merged beneath command-specific options (--id-tag, etc.)
so CLI flags always take precedence.

Implementation:
- New resolve-payload.ts: async resolver with validation (object check,
  empty check, JSON parse error with preview)
- Payload resolution integrated into runAction try/catch for clean
  error handling (no stack traces on user input errors)
- PAYLOAD_OPTION/PAYLOAD_DESC constants shared via payload.ts
- 11 unit tests for resolvePayload (inline, @file, edge cases)
- README and SKILL.md updated with examples

7 days agofeat(ui-cli): add registration and connector columns to station list, fix command...
Jérôme Benoit [Fri, 17 Apr 2026 22:33:34 +0000 (00:33 +0200)] 
feat(ui-cli): add registration and connector columns to station list, fix command semantics

Station list improvements:
- Add Registration column (Accepted/Pending/Rejected) after Hash ID
- Add Connectors column with per-connector status letters (1:A 2:O etc.)
- Two-letter abbreviations for ambiguous statuses (Fi/F, SE/SS)
- Remove dead countConnectors function, replace with formatConnectors
- Replace countConnectors tests with formatConnectors tests (7 tests)

Command description semantics:
- All OCPP: 'Send OCPP X' -> 'Request station(s) to send OCPP X'
- Connection: 'Open/Close WebSocket connection' -> '... to CSMS on station(s)'
- Connector: 'Lock/Unlock a connector' -> '... connector on station(s)'
- Transaction: 'Start/Stop a transaction' -> '... on station(s)'
- SKILL.md: 'send OCPP messages' -> 'trigger OCPP messages from stations to the CSMS'
- README: updated accordingly

7 days agorefactor(ui-cli): move StationListPayload to shared types, add resolution error context
Jérôme Benoit [Fri, 17 Apr 2026 21:43:43 +0000 (23:43 +0200)] 
refactor(ui-cli): move StationListPayload to shared types, add resolution error context

- Move StationListPayload from renderers.ts to types.ts to fix
  cross-layer dependency (action.ts no longer imports from output layer)
- Wrap hash prefix resolution errors with contextual message
  ('Failed to resolve hash ID prefixes: ...')

7 days agofeat(ui-cli): short hash prefix matching, human output formatters, embedded agent...
Jérôme Benoit [Fri, 17 Apr 2026 21:35:15 +0000 (23:35 +0200)] 
feat(ui-cli): short hash prefix matching, human output formatters, embedded agent skill

Short hash resolution:
- Resolve hash ID prefixes via station list lookup before command execution
- Skip resolution when all IDs are full-length (>= 48 chars, SHA-384)
- Clear error messages for ambiguous or unknown prefixes
- Silent mode suppresses spinner during internal resolution call

Human output formatters:
- Borderless table renderers for station list, template list, simulator state, performance stats
- Shared format utils: truncateId, statusIcon, wsIcon, fuzzyTime, countConnectors
- Truncate hash IDs in all human output tables (station list + success/failure)
- Type guards with Array.isArray + null checks for safe payload dispatch
- Handle supervisionUrls as string | string[], optional configuration
- Dynamic status capitalization, shared captureStream test helper
- 70 new tests (format + renderers), using canonical ChargingStationData type

Embedded agent skill:
- `skill show` prints embedded SKILL.md to stdout
- `skill install [--global] [--force]` writes to .agents/skills/
- SKILL.md embedded at build time via esbuild define
- Follows AgentSkills.io open standard

CLI option rename:
- Global --url renamed to --server-url (explicit intent)
- supervision set-url --url renamed to --supervision-url (no collision)

README restructured with prefix matching docs, output modes table,
station delete options, grouped simulator vs local commands

7 days agodocs: add CLI section to root README alongside Web UI
Jérôme Benoit [Fri, 17 Apr 2026 20:44:09 +0000 (22:44 +0200)] 
docs: add CLI section to root README alongside Web UI

7 days agofeat(ui): human-readable CLI output + shared type updates + --url collision fix
Jérôme Benoit [Fri, 17 Apr 2026 20:17:10 +0000 (22:17 +0200)] 
feat(ui): human-readable CLI output + shared type updates + --url collision fix

CLI output renderers:
- Add borderless table renderers for station list, template list,
  simulator state, and performance statistics
- Shared format utils: truncateId, statusIcon, wsIcon, fuzzyTime,
  countConnectors using canonical ConnectorEntry/EvseEntry types
- Type guards with Array.isArray + null checks for safe dispatch
- Handle supervisionUrls as string | string[] (defensive normalize)
- Optional configuration in SimulatorState guard (graceful degradation)
- Dynamic status capitalization in generic payload display
- Extract captureStream test helper to tests/helpers.ts
- 70 new tests (40 format + 30 renderers), 124 total CLI tests

Shared type fixes (ui-common):
- Add provisioned field to TemplateStatistics
- Add timestamp field to ChargingStationData
- Add configuration (optional) to SimulatorState

CLI option rename:
- Rename global --url to --server-url (explicit intent)
- Rename supervision set-url --url to --supervision-url (no collision)
- Update types, consumers, integration tests, README

7 days agofix(ui-cli): resolve --url option collision between global and supervision
Jérôme Benoit [Fri, 17 Apr 2026 18:38:42 +0000 (20:38 +0200)] 
fix(ui-cli): resolve --url option collision between global and supervision

Rename global --url to --server-url and supervision set-url --url to
--supervision-url to prevent Commander option shadowing. Update types,
action consumer, integration tests, and README documentation.

7 days agofix(tests): use async mock callbacks for all async method mocks
Jérôme Benoit [Fri, 17 Apr 2026 16:16:51 +0000 (18:16 +0200)] 
fix(tests): use async mock callbacks for all async method mocks

Sync functions returning Promises are not behaviorally equivalent to
async functions. Align all 40 mock callbacks across 14 test files to use
async when the real method is async.

7 days ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Fri, 17 Apr 2026 15:54:44 +0000 (15:54 +0000)] 
[autofix.ci] apply automated fixes

7 days agochore(deps): lock file maintenance (#1797)
renovate[bot] [Fri, 17 Apr 2026 15:51:44 +0000 (17:51 +0200)] 
chore(deps): lock file maintenance (#1797)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
8 days agofix(tests): sync mock signatures with real async implementations
Jérôme Benoit [Fri, 17 Apr 2026 15:44:28 +0000 (17:44 +0200)] 
fix(tests): sync mock signatures with real async implementations

Two mocks returned Promises from sync functions instead of using async,
mismatching the real method signatures of sendTransactionEvent and
requestHandler.

8 days agochore(deps): update all non-major dependencies (#1798)
renovate[bot] [Fri, 17 Apr 2026 15:37:59 +0000 (17:37 +0200)] 
chore(deps): update all non-major dependencies (#1798)

* chore(deps): update all non-major dependencies

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
8 days agofix(ocpp2): restore connector cleanup in TransactionEvent(Ended) response handler
Jérôme Benoit [Fri, 17 Apr 2026 15:33:03 +0000 (17:33 +0200)] 
fix(ocpp2): restore connector cleanup in TransactionEvent(Ended) response handler

Commit 710db15c removed cleanup from the Ended response handler assuming
callers always handle it. The UI broadcast channel passthrough bypasses
all caller-side cleanup, leaving connectors stuck in Occupied state.

Restore cleanupEndedTransaction in handleResponseTransactionEvent for the
Ended case, making the response handler the authoritative cleanup point
for all online paths. Add idempotency guard to cleanupEndedTransaction
so existing caller-side calls (needed for offline fallback) become no-ops
when the response handler already ran. Fix unconditional Available status
to respect ChangeAvailability(Inoperative) per G03.

8 days agofix(ui-web): force toggle button re-mount on navigation back to main view
Jérôme Benoit [Fri, 17 Apr 2026 14:50:18 +0000 (16:50 +0200)] 
fix(ui-web): force toggle button re-mount on navigation back to main view

resetToggleButtonState() only cleared localStorage but never triggered
ToggleButton re-mount, leaving its reactive state stuck on 'active'.
Watch route changes to CHARGING_STATIONS and call refresh() to re-mount
both the header toggle and CSTable children, ensuring all shared toggle
buttons re-read their (now cleared) localStorage entries.

8 days agofix(ui): use portable crypto API and async bootstrap pattern
Jérôme Benoit [Fri, 17 Apr 2026 14:27:49 +0000 (16:27 +0200)] 
fix(ui): use portable crypto API and async bootstrap pattern

- Replace node:crypto import with globalThis.crypto.randomUUID() in
  ui-common UUID utility for browser compatibility
- Rewrite ui-web main.ts with async bootstrap() pattern instead of
  top-level await, following Vue.js community best practices
- Remove unused ToastPlugin registration (all code uses useToast()
  composable directly)
- Add granular error handling for fetch, parse, and init phases

8 days ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Fri, 17 Apr 2026 10:07:20 +0000 (10:07 +0000)] 
[autofix.ci] apply automated fixes

8 days agofix(ui-web): fix remaining for...in on localStorage and remove dead test config
Jérôme Benoit [Fri, 17 Apr 2026 10:02:47 +0000 (12:02 +0200)] 
fix(ui-web): fix remaining for...in on localStorage and remove dead test config

- Replace for...in with Object.keys() in deleteLocalStorageByKeyPattern
- Remove dead $route/$router globalProperties from ChargingStationsView
  test (component now uses useRoute/useRouter composables via vi.mock)

8 days agorefactor(ui): global code quality pass
Jérôme Benoit [Fri, 17 Apr 2026 09:56:03 +0000 (11:56 +0200)] 
refactor(ui): global code quality pass

- Fix ToggleButton.vue: replace unsafe for...in on localStorage with
  Object.keys(), cache localStorage value (4 reads reduced to 1)
- Extract 42-line inline @change handler in ChargingStationsView.vue
  to handleUIServerChange method
- Move ServerFailureError from WebSocketClient.ts to errors.ts
  (proper separation of concerns, re-exported for backward compat)
- Simplify useExecuteAction callback param: remove union type,
  use only ExecuteActionCallbacks object; update call sites
- Remove unnecessary String() in cli table.ts template literals

8 days agorefactor(ui): second-pass factorization audit implementation
Jérôme Benoit [Fri, 17 Apr 2026 01:33:27 +0000 (03:33 +0200)] 
refactor(ui): second-pass factorization audit implementation

- Extend useExecuteAction with onSuccess callback support (backward-compatible)
- Migrate startSimulator/stopSimulator/deleteChargingStation to useExecuteAction
- Remove unused useToast imports from ChargingStationsView and CSData
- Extract .supervision-url CSS to shared.css as .input-url
- Add pickPresent helper for identity-key optional spreading in CLI
- Migrate atg.ts optional connectorIds and station.ts delete to helpers
- Add 12 payload helper tests (buildHashIdsPayload/pickDefined/pickPresent)
- Refactor main.ts config fetch from nested promises to async/await
- Remove 2 eslint-disable promise/no-nesting comments

8 days agorefactor(ui-web): remove duplicated tests for functions now in ui-common
Jérôme Benoit [Fri, 17 Apr 2026 01:16:19 +0000 (03:16 +0200)] 
refactor(ui-web): remove duplicated tests for functions now in ui-common

convertToBoolean, convertToInt, and UUID tests were duplicated between
ui-web and ui-common. Tests belong in the package that owns the code.
Canonical tests are in ui-common; removed duplicates from ui-web.

8 days agotest(ui): add tests for converters, websocket utils, and useFetchData
Jérôme Benoit [Fri, 17 Apr 2026 01:08:51 +0000 (03:08 +0200)] 
test(ui): add tests for converters, websocket utils, and useFetchData

- Add converters.test.ts in ui-common (26 tests for convertToBoolean/convertToInt)
- Add websocket.test.ts in ui-common (6 tests for getWebSocketStateName)
- Add useFetchData tests in ui-web (5 tests: success, error, loading guard, reset, no-callback)
- Guard onError callback in useFetchData with try/catch for robustness

8 days agofix(ui-web): use portable WebSocketReadyState in CSData isWebSocketOpen
Jérôme Benoit [Fri, 17 Apr 2026 00:58:43 +0000 (02:58 +0200)] 
fix(ui-web): use portable WebSocketReadyState in CSData isWebSocketOpen

Replace browser-specific WebSocket.OPEN with WebSocketReadyState.OPEN
from ui-common for environment portability.

8 days ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Fri, 17 Apr 2026 00:56:20 +0000 (00:56 +0000)] 
[autofix.ci] apply automated fixes

8 days agorefactor(ui): move generic utilities to ui-common and add useFetchData composable
Jérôme Benoit [Fri, 17 Apr 2026 00:53:23 +0000 (02:53 +0200)] 
refactor(ui): move generic utilities to ui-common and add useFetchData composable

- Move convertToBoolean(), convertToInt() to ui-common/src/utils/converters.ts
- Move getWebSocketStateName() to ui-common/src/utils/websocket.ts using
  portable WebSocketReadyState enum instead of browser WebSocket constants
- Add useFetchData() composable to deduplicate fetch-with-loading-guard
  pattern in ChargingStationsView (3 instances collapsed)
- All consumers import directly from ui-common, no re-exports

8 days agorefactor(ui-web): import randomUUID/validateUUID directly from ui-common
Jérôme Benoit [Fri, 17 Apr 2026 00:47:04 +0000 (02:47 +0200)] 
refactor(ui-web): import randomUUID/validateUUID directly from ui-common

Remove re-exports from composables/Utils.ts and composables/index.ts.
Consumers now import directly from ui-common.

8 days agochore(ui-web): remove unused focus-outline CSS class
Jérôme Benoit [Fri, 17 Apr 2026 00:43:51 +0000 (02:43 +0200)] 
chore(ui-web): remove unused focus-outline CSS class

8 days agofix(ui-web): remove orphaned CSS classes and guard onFinally in useExecuteAction
Jérôme Benoit [Fri, 17 Apr 2026 00:39:28 +0000 (02:39 +0200)] 
fix(ui-web): remove orphaned CSS classes and guard onFinally in useExecuteAction

- Remove dead connectors-table__row/column classes from CSConnector.vue
- Wrap onFinally callback in try/catch inside .finally() to prevent
  swallowing the original rejection error

8 days ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Fri, 17 Apr 2026 00:32:19 +0000 (00:32 +0000)] 
[autofix.ci] apply automated fixes

8 days agorefactor(ui): factorize shared code across ui packages
Jérôme Benoit [Fri, 17 Apr 2026 00:29:14 +0000 (02:29 +0200)] 
refactor(ui): factorize shared code across ui packages

- Centralize ConnectionError, extractErrorMessage, and protocol defaults in ui-common
- Remove UUID duplication from ui-web (import from ui-common instead)
- Extract shared CSS (data-table, action-header) into shared.css
- Add buildHashIdsPayload() and pickDefined() helpers to eliminate CLI command duplication
- Replace 12 repetitive OCPP command definitions with declarative registry
- Extend useExecuteAction() composable with onFinally support and migrate action components
- Remove backward-compat re-export shims; consumers import directly from ui-common

8 days agochore(deps): update node.js to v24.15.0 (#1796)
renovate[bot] [Fri, 17 Apr 2026 00:02:22 +0000 (02:02 +0200)] 
chore(deps): update node.js to v24.15.0 (#1796)

* chore(deps): update node.js to v24.15.0

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
8 days agofix(ui-common): forward close code/reason in mock WebSocket factory
Jérôme Benoit [Thu, 16 Apr 2026 21:24:18 +0000 (23:24 +0200)] 
fix(ui-common): forward close code/reason in mock WebSocket factory

close() and triggerClose() now accept optional code and reason params
and pass them through to the onclose callback (default: 1000, '').

8 days agodocs(ui-common): remove ADR section from README
Jérôme Benoit [Thu, 16 Apr 2026 21:22:16 +0000 (23:22 +0200)] 
docs(ui-common): remove ADR section from README

8 days ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Thu, 16 Apr 2026 21:09:53 +0000 (21:09 +0000)] 
[autofix.ci] apply automated fixes

8 days agodocs: add ADR for config loading strategy and ClientConfig derivation
Jérôme Benoit [Thu, 16 Apr 2026 21:06:03 +0000 (23:06 +0200)] 
docs: add ADR for config loading strategy and ClientConfig derivation

Update WebSocketClient usage example to use Protocol.UI and
ProtocolVersion['0.0.1'] enum values (matching the now-enum-typed
ClientConfig). Add Architecture Decisions section documenting:
- ADR 1: why CLI and Web have separate config loaders (different I/O)
- ADR 2: why ClientConfig = Omit<UIServerConfigurationSection, 'name'>

8 days agochore: hoist shared devDependencies to root workspace
Jérôme Benoit [Thu, 16 Apr 2026 21:05:15 +0000 (23:05 +0200)] 
chore: hoist shared devDependencies to root workspace

Remove @types/node, cross-env, prettier, rimraf, tsx, typescript from
ui/common, ui/web, and ui/cli package.json — they are already declared
in the root package.json. cli-specific deps (@types/ws, esbuild,
esbuild-plugin-clean) are kept in ui/cli.

8 days agotest(ui-common): create shared mock WebSocket factory
Jérôme Benoit [Thu, 16 Apr 2026 20:59:33 +0000 (22:59 +0200)] 
test(ui-common): create shared mock WebSocket factory

Create ui/common/tests/mocks.ts with createMockWebSocketLike() — a factory
returning a MockWebSocketLike with trigger methods (triggerOpen, triggerClose,
triggerError, triggerMessage) and sentMessages capture array. Replace the
65-line inline createMockWs() in WebSocketClient.test.ts with an import
from the shared factory.

8 days agorefactor(ui-common): generic WebSocket adapter factory with converter injection
Jérôme Benoit [Thu, 16 Apr 2026 20:39:18 +0000 (22:39 +0200)] 
refactor(ui-common): generic WebSocket adapter factory with converter injection

Create ui/common/src/client/adapter.ts — a generic createWsAdapter(ws, options)
factory that implements all WebSocketLike callback boilerplate once. Both adapters
become thin wrappers specifying only their platform-specific data converter:

- createBrowserWsAdapter: data => data as string, errorDefault: 'WebSocket error'
- createWsAdapter (CLI): toDataString() for Buffer/ArrayBuffer, errorDefault: 'Unknown error'

Net reduction: ~143 lines of duplicated boilerplate eliminated.
Add DataConverter type and tests for the generic factory.

8 days agorefactor(cli): extract extractErrorMessage utility
Jérôme Benoit [Thu, 16 Apr 2026 20:26:06 +0000 (22:26 +0200)] 
refactor(cli): extract extractErrorMessage utility

Create ui/cli/src/utils/errors.ts with extractErrorMessage(error: unknown): string
and replace 3 inline occurrences of the pattern across formatter.ts, json.ts,
and loader.ts.

8 days agorefactor(ui-common): derive ClientConfig and AuthenticationConfig from Zod schemas
Jérôme Benoit [Thu, 16 Apr 2026 20:22:56 +0000 (22:22 +0200)] 
refactor(ui-common): derive ClientConfig and AuthenticationConfig from Zod schemas

Replace two hand-written interfaces in client/types.ts with type aliases
derived from the Zod-inferred UIServerConfigurationSection:
- AuthenticationConfig = NonNullable<UIServerConfigurationSection['authentication']>
- ClientConfig = Omit<UIServerConfigurationSection, 'name'>

This eliminates duplication: protocol/version now carry enum types (Protocol,
ProtocolVersion) instead of loose strings. Update WebSocketClient.test.ts to
use Protocol.UI and ProtocolVersion['0.0.1'] enum values accordingly.

8 days agorefactor(ui): consolidate constants — remove timeout duplicate, centralize defaults
Jérôme Benoit [Thu, 16 Apr 2026 19:39:21 +0000 (21:39 +0200)] 
refactor(ui): consolidate constants — remove timeout duplicate, centralize defaults

- Remove dead duplicate UI_WEBSOCKET_REQUEST_TIMEOUT_MS from ui/web/src/composables/Constants.ts
- Add DEFAULT_HOST ('localhost') and DEFAULT_PORT (8080) to ui/common/src/constants.ts
- Update ui/cli/src/config/defaults.ts and loader.ts to import from ui-common
- Update ui/web/tests/unit/constants.ts to use DEFAULT_HOST/DEFAULT_PORT from ui-common
- Update ui/cli/tests/config.test.ts to import from ui-common

8 days agorefactor(ui-common): remove UIServerConfig alias, single canonical name
Jérôme Benoit [Thu, 16 Apr 2026 17:07:53 +0000 (19:07 +0200)] 
refactor(ui-common): remove UIServerConfig alias, single canonical name

UIServerConfigurationSection is the only exported name for the UI server
config type. The UIServerConfig alias added for backward compat is removed;
CLI consumers (loader.ts, lifecycle.ts) migrated to the canonical name.
README updated accordingly.

8 days agotest: add enum rejection tests and fix dynamic import in CLI test
Jérôme Benoit [Thu, 16 Apr 2026 16:33:37 +0000 (18:33 +0200)] 
test: add enum rejection tests and fix dynamic import in CLI test

- Add 2 edge-case tests for invalid protocol/version enum values
  (protocol: 'ws' and version: '2.0' are now correctly rejected)
- Replace unnecessary dynamic import with static import in
  lifecycle.test.ts for consistency with other test files

8 days agorefactor(ui-common): derive UIServerConfigurationSection from Zod schema
Jérôme Benoit [Thu, 16 Apr 2026 16:20:47 +0000 (18:20 +0200)] 
refactor(ui-common): derive UIServerConfigurationSection from Zod schema

Make uiServerConfigSchema the single source of truth for the UI server
configuration shape. The hand-written interface in ConfigurationType.ts
is removed; UIServerConfigurationSection is now inferred via z.infer<>.

- Tighten schema: protocol and version use z.enum() instead of z.string()
  (follows existing z.enum(AuthenticationType) pattern, Zod v4)
- Export UIServerConfigurationSection as the primary inferred type
- Export UIServerConfig as a backward-compat alias
- ConfigurationType.ts imports UIServerConfigurationSection from schema.ts
  and re-exports only ConfigurationData
- CLI defaults.ts uses Protocol.UI and ProtocolVersion['0.0.1'] enum values
  instead of plain string literals to satisfy the tightened type
- CLI lifecycle.test.ts updated to use enum values in typed config literal

Zero duplicate field definitions. All quality gates pass.

9 days agotest(web): cover abort branches in UIClient to meet coverage threshold
Jérôme Benoit [Thu, 16 Apr 2026 14:46:01 +0000 (16:46 +0200)] 
test(web): cover abort branches in UIClient to meet coverage threshold

9 days agorefactor: fix phantom errorMessage, merge imports, harmonize Docker configs
Jérôme Benoit [Thu, 16 Apr 2026 13:27:13 +0000 (15:27 +0200)] 
refactor: fix phantom errorMessage, merge imports, harmonize Docker configs

- stopTransaction: use responsesFailed instead of phantom errorMessage field
- ConfigurationType: merge duplicate imports from UIProtocol
- Docker: remove pnpm@latest (use packageManager field), remove depth 0 no-op
- Docker: add EXPOSE to both Dockerfiles (8080 simulator, 3030 dashboard)
- Docker: exec in run.sh for proper signal propagation

9 days agofix(common): restore wsState type as numeric literal union
Jérôme Benoit [Thu, 16 Apr 2026 01:41:34 +0000 (03:41 +0200)] 
fix(common): restore wsState type as numeric literal union

9 days ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Thu, 16 Apr 2026 01:33:25 +0000 (01:33 +0000)] 
[autofix.ci] apply automated fixes

9 days agorefactor: consolidate all types into ui-common — single import source
Jérôme Benoit [Thu, 16 Apr 2026 01:30:18 +0000 (03:30 +0200)] 
refactor: consolidate all types into ui-common — single import source

9 days agofix(web): copy workspace node_modules, harmonize browser adapter, add frozen-lockfile
Jérôme Benoit [Thu, 16 Apr 2026 01:10:16 +0000 (03:10 +0200)] 
fix(web): copy workspace node_modules, harmonize browser adapter, add frozen-lockfile

9 days agofix(deps): update dependency basic-ftp to ^5.3.0 (#1795)
renovate[bot] [Thu, 16 Apr 2026 01:02:16 +0000 (03:02 +0200)] 
fix(deps): update dependency basic-ftp to ^5.3.0 (#1795)

* fix(deps): update dependency basic-ftp to ^5.3.0

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
9 days agorefactor(common): portable btoa, eliminate UIClient duplication, fix Docker deps
Jérôme Benoit [Thu, 16 Apr 2026 00:59:20 +0000 (02:59 +0200)] 
refactor(common): portable btoa, eliminate UIClient duplication, fix Docker deps

9 days agofix(web): widen Docker context to monorepo root for ui-common workspace dep
Jérôme Benoit [Thu, 16 Apr 2026 00:19:36 +0000 (02:19 +0200)] 
fix(web): widen Docker context to monorepo root for ui-common workspace dep

9 days agofix(common): include Date in JsonPrimitive, restore Date fields
Jérôme Benoit [Wed, 15 Apr 2026 23:54:33 +0000 (01:54 +0200)] 
fix(common): include Date in JsonPrimitive, restore Date fields

9 days agorefactor(web): eliminate type barrel — direct imports from ui-common and source modules
Jérôme Benoit [Wed, 15 Apr 2026 23:43:43 +0000 (01:43 +0200)] 
refactor(web): eliminate type barrel — direct imports from ui-common and source modules

9 days agofix(web): prevent ghost events after server switch, fix CloseEvent type, improve...
Jérôme Benoit [Wed, 15 Apr 2026 23:14:24 +0000 (01:14 +0200)] 
fix(web): prevent ghost events after server switch, fix CloseEvent type, improve error extraction

9 days ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Wed, 15 Apr 2026 22:55:02 +0000 (22:55 +0000)] 
[autofix.ci] apply automated fixes

9 days agorefactor(web): migrate types to ui-common + UIClient internals to WebSocketClient
Jérôme Benoit [Wed, 15 Apr 2026 22:48:39 +0000 (00:48 +0200)] 
refactor(web): migrate types to ui-common + UIClient internals to WebSocketClient

9 days agorefactor(common): export browser adapter from barrel
Jérôme Benoit [Wed, 15 Apr 2026 21:03:38 +0000 (23:03 +0200)] 
refactor(common): export browser adapter from barrel

9 days agofeat(common): add notification callback support to WebSocketClient
Jérôme Benoit [Wed, 15 Apr 2026 21:02:00 +0000 (23:02 +0200)] 
feat(common): add notification callback support to WebSocketClient

9 days agofeat(common): add browser WebSocket adapter
Jérôme Benoit [Wed, 15 Apr 2026 20:46:47 +0000 (22:46 +0200)] 
feat(common): add browser WebSocket adapter

9 days agofix(cli): validate connect timeout budget — reject NaN/0/negative before race
Jérôme Benoit [Wed, 15 Apr 2026 20:15:42 +0000 (22:15 +0200)] 
fix(cli): validate connect timeout budget — reject NaN/0/negative before race

9 days agochore(deps): lock file maintenance (#1794)
renovate[bot] [Wed, 15 Apr 2026 20:11:49 +0000 (22:11 +0200)] 
chore(deps): lock file maintenance (#1794)

* chore(deps): lock file maintenance

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
9 days agorefactor(cli): complete remaining audit items — validate status, extract helpers...
Jérôme Benoit [Wed, 15 Apr 2026 19:50:17 +0000 (21:50 +0200)] 
refactor(cli): complete remaining audit items — validate status, extract helpers, add 8 test cases

- Fix #28: Validate ResponsePayload status is string before cast in handleMessage()
- Fix #43: Extract settleHandler() private method to reduce duplication
- Fix #41: Normalize createMockWS → createMockWs for consistency
- Fix #39: Extract captureStream() parameterized helper to replace captureStdout/captureStderr
- Fix #47: Filter empty arrays in displayGenericPayload to avoid spurious output
- Test #21-#22: Add ConnectionError edge cases (empty cause, non-Error cause)
- Test #23: Add ServerFailureError without hashIdsFailed
- Test #24: Add post-connect onerror rejection test
- Test #25: Add displayGenericPayload failure path test
- Test #26: Add NaN and Infinity timeout validation tests

All tests pass, zero lint warnings.

9 days agofix(deps): update all non-major dependencies (#1792)
renovate[bot] [Wed, 15 Apr 2026 19:47:13 +0000 (21:47 +0200)] 
fix(deps): update all non-major dependencies (#1792)

* fix(deps): update all non-major dependencies

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
9 days agorefactor(cli): fix onerror ErrorEvent handling, DRY adapter types, simplify factory
Jérôme Benoit [Wed, 15 Apr 2026 19:39:12 +0000 (21:39 +0200)] 
refactor(cli): fix onerror ErrorEvent handling, DRY adapter types, simplify factory

9 days agorefactor(cli): second-pass audit fixes — DRY table helper, remove slop, strengthen...
Jérôme Benoit [Wed, 15 Apr 2026 19:23:52 +0000 (21:23 +0200)] 
refactor(cli): second-pass audit fixes — DRY table helper, remove slop, strengthen assertions

9 days agorefactor(cli): audit fixes — validate timeoutMs, extract mock factory, add comments
Jérôme Benoit [Wed, 15 Apr 2026 19:09:36 +0000 (21:09 +0200)] 
refactor(cli): audit fixes — validate timeoutMs, extract mock factory, add comments

9 days ago[autofix.ci] apply automated fixes
autofix-ci[bot] [Wed, 15 Apr 2026 18:13:28 +0000 (18:13 +0000)] 
[autofix.ci] apply automated fixes

9 days agotest(cli): fix lifecycle test structure — single top-level describe
Jérôme Benoit [Wed, 15 Apr 2026 18:06:40 +0000 (20:06 +0200)] 
test(cli): fix lifecycle test structure — single top-level describe

9 days agofix(cli): replace unsafe WebSocket double cast with typed adapter
Jérôme Benoit [Wed, 15 Apr 2026 17:56:08 +0000 (19:56 +0200)] 
fix(cli): replace unsafe WebSocket double cast with typed adapter

9 days agofix(cli): replace unsafe WebSocket double cast with typed adapter
Jérôme Benoit [Wed, 15 Apr 2026 17:53:17 +0000 (19:53 +0200)] 
fix(cli): replace unsafe WebSocket double cast with typed adapter

9 days agofeat(cli): display failure reasons in human-readable table output
Jérôme Benoit [Wed, 15 Apr 2026 17:53:08 +0000 (19:53 +0200)] 
feat(cli): display failure reasons in human-readable table output

9 days agofix(deps): update dependency ora to v9 (#1793)
renovate[bot] [Wed, 15 Apr 2026 17:11:24 +0000 (19:11 +0200)] 
fix(deps): update dependency ora to v9 (#1793)

* fix(deps): update dependency ora to v9

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
9 days agofeat(ui): add CLI client and shared UI common library (#1789)
Jérôme Benoit [Wed, 15 Apr 2026 16:59:22 +0000 (18:59 +0200)] 
feat(ui): add CLI client and shared UI common library (#1789)

* feat(ui): scaffold ui/common and ui/cli workspaces

- Add ui/common workspace with shared protocol types, SRPC WebSocket client,
  UUID utilities, config types, and Zod validation schemas (21 tests, 0 errors)
- Add ui/cli workspace scaffolding with all configuration files
- Register both workspaces in pnpm-workspace.yaml
- Update .prettierignore, release-please config and manifest
- Add build-common (library, single env) and build-cli (3x3 matrix) CI jobs
- Add formatting steps for new workspaces in autofix.yml
- Add sonar-project.properties for both workspaces

* feat(cli): implement CLI core infrastructure

- Commander.js entry point with 10 subcommand groups (simulator, station,
  template, connection, connector, atg, transaction, ocpp, performance,
  supervision)
- Config loading with lilconfig + Zod validation + merge precedence
  (defaults < config file < CLI flags)
- Output formatters: JSON (--json flag) and table (human-readable)
- WS client lifecycle: executeCommand() connects, sends, receives,
  disconnects; registerSignalHandlers() for SIGINT/SIGTERM
- Typed error classes: ConnectionError, AuthenticationError,
  TimeoutError, ServerError
- esbuild bundle with version injection and shebang
- 23 unit tests passing, zero lint warnings

* feat(cli): implement all 35 UI protocol command groups

All 35 ProcedureName procedures exposed as CLI subcommands:
- simulator: state, start, stop (3)
- station: list, start, stop, add, delete (5)
- template: list (1)
- connection: open, close (2)
- connector: lock, unlock (2)
- atg: start, stop (2)
- transaction: start, stop (2)
- ocpp: authorize, boot-notification, data-transfer, heartbeat,
  meter-values, status-notification, firmware-status-notification,
  diagnostics-status-notification, security-event-notification,
  sign-certificate, notify-report, notify-customer-information,
  log-status-notification, get-15118-ev-certificate,
  get-certificate-status, transaction-event (16)
- performance: stats (1)
- supervision: set-url (1)

Shared runAction() helper DRYs up all command action handlers.

* test(cli): add integration tests

- 8 integration tests covering --help, --version, subcommand help,
  connection error handling, JSON mode, and missing required options
- Separate test:integration script targeting tests/integration/
- Unit tests narrowed to tests/*.test.ts (no integration overlap)

* docs(cli): add README for ui/cli and ui/common

- ui/cli/README.md: installation, configuration, all command groups
  with examples, global options, exit codes, environment variables,
  available scripts
- ui/common/README.md: exported API reference (types, WebSocketClient,
  config validation, UUID utilities)

* [autofix.ci] apply automated fixes

* fix(cli): improve error handling for config and connection failures

- Catch config file ENOENT in loader.ts and throw clean error message
- Move loadConfig inside try/catch in runAction to prevent stack traces
- Use event.error in WebSocketClient onerror for better error propagation
- Separate connect errors from request errors in lifecycle.ts
- Include cause message in ConnectionError for descriptive output

* fix(cli): address PR review feedback

- Fix onerror stale after connect: replace with persistent error handler
  that fails all pending sendRequest promises immediately on WS error
- Fix dead code: call registerSignalHandlers() in cli.ts for SIGINT/SIGTERM
- Fix JSON error output: write to stdout (not stderr) in --json mode to
  match documented contract and enable scripting with 2>/dev/null
- Fix process.exit() in action.ts: use process.exitCode for proper async
  cleanup and testability
- Fix Map iteration: use snapshot+clear pattern in clearHandlers/failAllPending
- Fix empty array edge case in table formatter: check .length === 0
- Fix README: merge exit codes 1+2 into single row (Commander uses 1)
- Fix CI: add needs: build-common to build-cli job

* fix(cli): address second round of PR review feedback

- Fix ServerFailureError: WebSocketClient.handleMessage now rejects
  with a typed Error (carrying the ResponsePayload) instead of a raw
  object, preventing [object Object] in CLI output
- Fix table formatter: FAILURE responses now display hashIdsFailed/
  hashIdsSucceeded tables instead of early-returning with minimal info
- Fix auth schema: Zod refinement requires username+password when
  authentication enabled with protocol-basic-auth
- Fix AuthenticationConfig.type: use AuthenticationType enum instead
  of plain string for compile-time safety
- Fix signal handlers: use process.exitCode instead of process.exit()
  so finally blocks in executeCommand can run cleanup

* fix(cli): restore process.exit in signal handlers, fix type mismatch, remove dead code

- Signal handlers: restore process.exit() — setting only process.exitCode
  keeps the process alive since registering SIGINT listener removes Node's
  default termination behavior
- BroadcastChannelResponsePayload: align field name with server wire format
  (hashId: string | undefined, not id: string)
- Remove unused DEFAULT_TIMEOUT_MS export from defaults.ts

* refactor(ui-common): extract WS timeout to shared constant

Move UI_WEBSOCKET_REQUEST_TIMEOUT_MS from a private constant in
WebSocketClient.ts to ui/common/src/constants.ts as a single source
of truth, exported via barrel for consumers.

* refactor(cli): merge duplicate ui-common and commander imports

Consolidate split type/value imports from the same module into single
import statements using inline type syntax (`import { type X, Y }`).
Resolves SonarCloud 'imported multiple times' warning.

* refactor(cli): use Number.parseInt instead of parseInt

Resolves SonarCloud 'Prefer Number.parseInt over parseInt' warning.

* refactor(cli): remove unnecessary Command alias from commander import

Use import { Command } from 'commander' directly instead of aliasing
to Cmd. Single import serves both type and value usage.

* chore: reorder ui workspaces consistently (common, cli, web)

Apply dependency-first ordering across all config files:
pnpm-workspace.yaml, .prettierignore, release-please config/manifest,
ci.yml job order, and autofix.yml step order.

* refactor(ui-common): widen validateUUID to accept unknown

Align with ui/web pattern — move typeof string check into validateUUID
itself, removing redundant guard at call sites. Add tests for
non-string inputs (number, null, undefined, object, boolean).

* fix(cli): use Vercel CLI pattern for graceful signal shutdown

Replace AbortController with module-level activeClient/activeSpinner
refs + cleanupInProgress guard (Vercel CLI pattern). Signal handler
stops spinner, disconnects WS, then process.exit(130/143). Simpler,
battle-tested, correct for batch request-response CLI.

* chore: reorder linked-versions components (common, cli, web)

* style(ci): add blank line between all job definitions in ci.yml

* refactor(cli): rename parseIntList to parseCommaSeparatedInts

* fix(cli): validate connector IDs input and wrap config search errors

- parseCommaSeparatedInts now rejects NaN values with a clear error
  message instead of silently sending garbage to the server
- lilconfig search() path now wrapped in try/catch like the explicit
  config path, giving consistent error messages for malformed configs

* feat(cli): standalone build + XDG-only config + install script

- Bundle all dependencies into single 504KB dist/cli.js (no node_modules
  needed at runtime). Only ws native addons (bufferutil, utf-8-validate)
  are external — ws falls back to pure JS automatically.
- Replace lilconfig with direct XDG config file reading:
  ${XDG_CONFIG_HOME:-~/.config}/evse-cli/config.json
- Remove lilconfig dependency
- Add install.sh: builds CLI, copies to ~/.local/bin/evse-cli, creates
  default XDG config, warns if ~/.local/bin not in PATH
- Isolate ALL config tests from host env via XDG_CONFIG_HOME in
  beforeEach/afterEach + add XDG auto-discovery happy path test
- Guard against JSON array in config file
- Update README: standalone install instructions + XDG config location

* [autofix.ci] apply automated fixes

* fix(cli): address review feedback round 3

WebSocketClient:
- Validate responsePayload shape before casting (guard against [uuid, null])
- Reject connect() Promise if socket closes before onopen fires
- Add tests for both edge cases

CLI:
- Validate ws:/wss: URL scheme in parseServerUrl
- Output ServerFailureError.payload via formatter (show hashIdsFailed details)
- Extract shared parseInteger() validator — reject NaN with clear error
- Remove dead error types (AuthenticationError, ServerError, TimeoutError)
- Chain build in test:integration script
- Remove unreachable FAILURE branch in outputTable

Schema:
- Require password.length > 0 in auth refinement (reject empty string)

* ci: remove redundant lint:fix from autofix workflow

pnpm format already runs eslint --cache --fix, making the separate
pnpm lint:fix step redundant in all three ui workspaces.

* refactor(cli): remove unnecessary comment in outputTable

* fix(cli): don't override template defaults in station add, reject array config

- station add: only include autoStart, persistentConfiguration,
  ocppStrictCompliance, deleteConfiguration in payload when explicitly
  passed — lets server use template defaults otherwise
- config loader: reject uiServer array with clear error instead of
  silently spreading array keys into object

* refactor(cli): remove unused terminal-link dep and dead exports

- Remove terminal-link from dependencies (never imported)
- Remove unused exports: printSuccess, printWarning, printInfo (human.ts),
  outputTableList (table.ts)
- Remove corresponding test for printSuccess

* [autofix.ci] apply automated fixes

* fix(ui-common): reject malformed payloads, replace ReadyState with enum

- handleMessage: reject pending handler immediately when server sends
  response with matching UUID but missing status field, instead of
  silently dropping and waiting for 60s timeout
- Replace ReadyState type alias with WebSocketReadyState const enum
- Remove redundant ReadyState type (duplicated enum semantics)

* fix(cli): add connection timeout, remove dead defaultUIServerConfig

- Wrap client.connect() with Promise.race timeout to prevent infinite
  hang when server accepts TCP but never completes WS handshake
- Remove unused defaultUIServerConfig export from defaults.ts

* fix(cli): clear connection timeout timer on success

* refactor(ui-common): expose url getter, remove duplicate ConfigurationType

- WebSocketClient: make buildUrl() a public url getter so consumers
  use the canonical URL instead of reconstructing it
- lifecycle.ts: use client.url instead of building URL independently
- Remove ConfigurationType.ts: UIServerConfigurationSection is now
  a type alias for the Zod-inferred UIServerConfig (single source)

* fix(cli): display failure status instead of misleading Success

displayGenericPayload now checks payload.status before printing — a
failure response without hashIds shows the red status line instead of
a green checkmark.

* fix: align ui/web ResponsePayload with server, reject non-object config

- ui/web ResponsePayload: replace incorrect hashIds with
  hashIdsFailed/hashIdsSucceeded/responsesFailed matching server schema
- cli config loader: reject non-object uiServer values with clear error
  instead of silently falling back to defaults

* fix: reject non-object config files, merge duplicate import

- Config loader: throw on primitive JSON config values (42, "hello")
  instead of silently falling back to defaults
- Merge duplicate UIProtocol.ts import in ui/common/src/client/types.ts

* fix: suppress dangling connect rejection, remove dead Zod defaults

- Attach .catch() to connect() promise to prevent unhandled rejection
  when the timeout wins Promise.race and disconnect triggers onclose
- Remove .default() calls from Zod schema — the CLI loader always
  provides all fields via its canonical defaults map, making Zod
  defaults dead code paths

* fix(cli): preserve error cause in config loader for debuggability

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
10 days agochore(deps): lock file maintenance (#1790)
renovate[bot] [Tue, 14 Apr 2026 20:05:30 +0000 (22:05 +0200)] 
chore(deps): lock file maintenance (#1790)

* chore(deps): lock file maintenance

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
11 days agochore(deps): update all non-major dependencies (#1791)
renovate[bot] [Tue, 14 Apr 2026 14:19:14 +0000 (16:19 +0200)] 
chore(deps): update all non-major dependencies (#1791)

* chore(deps): update all non-major dependencies

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2 weeks agochore(deps): update pnpm/action-setup action to v6 (#1788)
renovate[bot] [Sat, 11 Apr 2026 14:04:50 +0000 (16:04 +0200)] 
chore(deps): update pnpm/action-setup action to v6 (#1788)

* chore(deps): update pnpm/action-setup action to v6

* [autofix.ci] apply automated fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2 weeks agofix(deps): update all non-major dependencies to ^6.6.13 (#1787)
renovate[bot] [Sat, 11 Apr 2026 11:28:44 +0000 (13:28 +0200)] 
fix(deps): update all non-major dependencies to ^6.6.13 (#1787)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2 weeks agodocs: clean up agent memories — declarative facts only, no narrative justifications
Jérôme Benoit [Fri, 10 Apr 2026 18:11:42 +0000 (20:11 +0200)] 
docs: clean up agent memories — declarative facts only, no narrative justifications

2 weeks agorefactor: use charging-station barrel for buildConfigKey in OCPPServiceUtils
Jérôme Benoit [Fri, 10 Apr 2026 16:58:41 +0000 (18:58 +0200)] 
refactor: use charging-station barrel for buildConfigKey in OCPPServiceUtils

2 weeks agorefactor(tests): replace re-export hub with direct imports
Jérôme Benoit [Fri, 10 Apr 2026 16:27:37 +0000 (18:27 +0200)] 
refactor(tests): replace re-export hub with direct imports

Remove ChargingStationTestUtils.ts re-export hub and update all 73
test files to import directly from their defining modules
(StationHelpers, TestLifecycleHelpers, MockCaches, MockWebSocket).

Fix __testable__ barrel bypass in OCPP20RequestService-CallChain test.

Harmonize Web UI test imports to use composables/types barrels
consistently and export validateUUID from composables barrel.

Add 'Direct Imports' convention to TEST_STYLE_GUIDE.md §6.

2 weeks agodocs: update AuthHelpers description in project overview memory
Jérôme Benoit [Fri, 10 Apr 2026 16:03:50 +0000 (18:03 +0200)] 
docs: update AuthHelpers description in project overview memory

2 weeks agofeat(ocpp): implement Local Auth List Management Profile (GetLocalListVersion, SendLo...
Jérôme Benoit [Fri, 10 Apr 2026 15:58:43 +0000 (17:58 +0200)] 
feat(ocpp): implement Local Auth List Management Profile (GetLocalListVersion, SendLocalList) (#1782)

* feat(ocpp): add local auth list types for OCPP 1.6 and 2.0

* feat(ocpp): add InMemoryLocalAuthListManager implementation

* feat(ocpp): export local auth list types from main types barrel

* feat(ocpp): wire LocalAuthListManager into auth subsystem

* feat(ocpp/2.0): implement GetLocalListVersion and SendLocalList handlers

* docs: mark Local Auth List Management commands as implemented

* style: apply formatting to local auth list test file

* [autofix.ci] apply automated fixes

* fix(ocpp): enforce VersionMismatch and size limits in SendLocalList handlers

* fix(ocpp): address PR review feedback on local auth list implementation

* fix(ocpp): improve robustness and consistency in local auth list handlers

* fix(ocpp): fix config key, atomicity, and version validation in local auth list

* fix(ocpp): harmonize validation ordering and improve type safety in local auth list

* fix(ocpp): add error logging to OCPP 1.6 local auth list catch blocks

* fix(ocpp): use enum values for auth status and fix OCPP 2.0 config key semantics

* refactor(ocpp): rename DEFAULT_IDTAG to OCPP_DEFAULT_IDTAG for consistent naming

* fix(test): use convertToBoolean in mock getLocalAuthListEnabled for production parity

* fix(ocpp): align OCPP16UpdateStatus naming and add maxLocalAuthListEntries validation

* fix(ocpp): gate maxLocalAuthListEntries validation on localAuthListEnabled, not cache

* refactor(ocpp): convert LocalAuthListManager interface from async to sync

* refactor(ocpp): remove redundant async from OCPPAuthServiceImpl.authorize()

* fix(ocpp): remove stale LocalAuthListManager implementation note in RemoteAuthStrategy

* refactor(ocpp): use barrel imports for cross-component boundaries

Replace direct file imports with barrel (index.ts) imports when crossing
component boundaries (ocpp/ → charging-station/, auth/ → ocpp/).
Add Entries to OCPP20RequiredVariableName enum.
Sync LocalAuthListCtrlr.Entries variable after SendLocalList.

* fix(ocpp): update stale JSDoc in MockFactories and AuthComponentFactory

* feat(ocpp-server): add GetLocalListVersion and SendLocalList command support

* fix(test): reuse getConfigurationKey in mock getLocalAuthListEnabled

* refactor(test): use upsertConfigurationKey in OCPP 2.0 local auth list tests

* refactor(ocpp): move readMaxLocalAuthListEntries to AuthHelpers

* refactor(ocpp): move maxLocalAuthListEntries reading to version adapters

* docs: update agent memories with local auth list, QMD integration, and convention fixes

* refactor(ocpp): widen AuthStrategy.authenticate to allow sync return and remove eslint-disable

* fix(test): remove unnecessary async from LocalAuthStrategy test callbacks

* fix(ocpp): address review feedback on volatile Entries, circular dep, and interface

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2 weeks agodocs: update agent memories with local auth list, QMD integration, and convention...
Jérôme Benoit [Fri, 10 Apr 2026 14:27:22 +0000 (16:27 +0200)] 
docs: update agent memories with local auth list, QMD integration, and convention fixes

2 weeks agofix(deps): update all non-major dependencies (#1785)
renovate[bot] [Fri, 10 Apr 2026 09:32:13 +0000 (11:32 +0200)] 
fix(deps): update all non-major dependencies (#1785)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>