feat: dial-unified-config — dial-cli (Picocli + Quarkus CLI)#1531
Open
siarhei-fedziukovich wants to merge 80 commits into
Open
feat: dial-unified-config — dial-cli (Picocli + Quarkus CLI)#1531siarhei-fedziukovich wants to merge 80 commits into
siarhei-fedziukovich wants to merge 80 commits into
Conversation
Contributor
Author
|
/deploy-review
|
Contributor
Author
|
/deploy-review
|
Contributor
Author
|
/deploy-review
|
ee00e6c to
a2400d6
Compare
|
/deploy-review
|
d1b1754 to
a4fa152
Compare
|
/deploy-review
|
|
/deploy-review
|
|
/deploy-review
|
|
/deploy-review
|
Adds the foundational :cli sibling Gradle module: Quarkus 3.16.4 + Picocli @topcommand entrypoint with design 05 §1 global flags, env/get stub subcommands, ~/.dial-cli/config.yaml profile loader (Jackson YAML, snake_case), and API-key resolution chain (env var → --api-key-file → no-echo prompt). Keystore tier deferred to post-MVP per slice 1C.0 row B1 — ships with auth login once OIDC lands. JVM-mode only per IMPLEMENTATION.md §3.4. Design anchors: 05 §1, §2, §6 Tests: cli/src/test/.../{DialCliSmokeTest,ProfileLoaderTest,ApiKeyResolverTest}.java Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ferral Slice 1C.0 (`feat: 1C.0: bootstrap :cli module with Picocli + Quarkus skeleton`, ff2ae5d) ships JVM-mode CLI bootstrap. Row updated: status ✅, commit pinned, scope-narrowing footnote added explaining the architect-plan halt — Ambiguity A (chain order = design 06 §2.1) and Ambiguity B1 (keystore tier deferred until `auth login` ships post-MVP). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires four Phase-1 env subcommands on the 1C.0 skeleton: list (sorted, current marked '*'), current (--env > defaults.env), use (validates env exists, atomic-rename save), check (config-only — reachability probe deferred to 1C.2). Adds ProfileLoader.save + ApiKeyResolver.describeSource. Design anchors: 05 §1 Tests: cli/src/test/.../EnvCommandTest.java + extensions Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…v-check note Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Establishes HTTP read pattern: CliHttpClient (JDK, redirects normal, Api-Key header), inline output formatter (json/yaml/table — NAME+ENDPOINT cols), ModelCommand Get/List + GetCommand dispatch. identifierToPath rejects ambiguous partial canonical IDs and URL-encodes simple names. HTTP→exit codes per 06 §2.8 (401/403→3, 404→4, 5xx→1). Design anchors: 05 §1; 06 §2.2 Tests: ModelCommandTest, CliHttpClientTest Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ecisions Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extracts EntityReader from ModelCommand; adds 8 per-type command classes (applications, toolsets, interceptors, roles, keys, routes, schemas, settings) as thin shims. Per-type bucket map (public/ vs platform/) + per-type table shape (NAME-only default, models keep NAME+ENDPOINT). SettingsCommand is singleton — Get only, no name arg. GetCommand alias extended to all 9 plurals. listEntities warns when hasMore=true. identifierToPath rejects unknown types and ambiguous partial canonical IDs. Files/prompts/conversations deferred (not in 1C.3 dep set). Design anchors: 05 §1 Tests: EntityReaderTypesTest (13), DialCliSmokeTest extensions (3 new) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds ExportCommand using EntityReader.resolveEnv (promoted package-private) + CliHttpClient.get(path, accept) overload. Format negotiation via global -o flag: yaml→application/yaml, json/table→application/json. -f/--output-file writes to file (creates parent dirs, rejects directory paths). UTF-8 charset pinned in BodyHandlers.ofString to avoid mojibake on non-ASCII entity names. HTTP error → exit code per 06 §2.8. Design anchors: 05 §1; 03 §1 Tests: ExportCommandTest (8 cases) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two GETs to /v1/admin/export per env, structural Jackson-tree diff with dotted path notation. JsonDiff utility (added/removed/changed; arrays opaque). DiffCommand uses EntityReader.resolveEnv(parent, spec, explicitEnv) overload — global --api-url override is ignored when explicit env is given (prevents diff hitting the same URL twice). Path-only output; operators use 'dial-cli get -o yaml' for values. Design anchors: 05 §1 Tests: JsonDiffTest (10), DiffCommandTest (9) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CompletionCommand uses Picocli's AutoComplete.bash() for bash and zsh (zsh-bash compat), rejects fish with exit 2 (Picocli has no fish generator; documented for follow-up). Unknown shell or missing arg returns exit 2 with helpful message. Design anchors: 05 §1 Tests: CompletionCommandTest (6 cases — bash, zsh, all-subcommands, fish, unknown, missing) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the first CLI write command. EntityWriter sibling helper handles canonical-id parsing (rejects simple names per design 05 §1), JSON/YAML file loading via extension-sniff, --dry-run local preview, and POST exit-code mapping (0/5/2/3 per 06 §2.8). CliHttpClient gains post() plus 409->5 / 400->2 mappings; Response record exposes ETag for future --if-match (2C.1). Design anchors: 05 §1, 06 §2.4, §2.8 Tests: cli/src/test/.../ModelCommandTest.java, CliHttpClientTest.java Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Update subcommand to ModelCommand. EntityWriter.updateEntity does
GET → local-merge → PUT, auto-threading the GET ETag as If-Match (operator
override via --if-match). --set k=v parses dotted paths, JSON-coerces
values, deep-merges into the GET body. CliHttpClient gains put(...) and
toExitCode now maps 412→6 per 06 §2.8.
Design anchors: 05 §1 lines 58, 88-95; 06 §2.4 lines 326-381
Tests: cli/src/test/java/com/epam/aidial/cli/{ModelCommandTest,http/CliHttpClientTest}.java
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces AuthType enum (API_KEY) so auth type is validated at deserialization time instead of relying on raw strings. Configures YAMLMapper with case-insensitive enum reads and lowercase enum writes to stay compatible with existing YAML profiles. Test deps de-versioned to resolve from the BOM. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
buildUri now uses the 7-arg URI constructor so path segments and query values are percent-encoded correctly (spaces as %20, not +). All HTTP methods gain no-query overloads so callers without query params need not pass null. Methods that do carry a query (entity listing with limit=100) pass it as a separate argument instead of embedding it in the path string. URLEncoder.encode() removed from EntityReader and EntityWriter — encoding is now CliHttpClient's responsibility exclusively. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…olution Remove the hard-coded Picocli defaultValue so --output is null when unset, then resolve via OutputFormatResolver: CLI flag > defaults.output in config > TABLE fallback. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Switching the active environment via `dial env use` previously round-tripped the config through Jackson serialization, which stripped comments, blank lines, and !if/!for custom tags from the templates section. Introduce YamlPatcher, a text-level YAML patcher that walks the raw file line-by-line using an indentation-based state machine and updates only the exact dot-path requested (e.g. defaults.env), leaving every other byte unchanged. ProfileLoader.saveDefaultEnv delegates to it, and the old full-profile save() is removed since it is no longer called from production code. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…TON_TYPES in GetCommand Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…TON_TYPES in GetCommand Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… missing-arg check to picocli, use enhanced switch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
--verbose was declared but never referenced anywhere in the codebase. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Java's HttpClient defaults to HTTP/2; servers that only support HTTP/1.1 reject the upgrade with 505. Pinning to HTTP_1_1 fixes all CLI requests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CliHttpClient: add 5-param put() overload with If-None-Match support
- EntityWriter: POST → PUT with If-None-Match:* for addEntity; map 412 → exit 5; drop 'source' from projection fields
- EntityReader: listings moved to /v1/metadata/{type}/{bucket}/ (nextToken pagination); add readConfigFileEntity, listConfigFileEntities, readConfigFileSingleton for /v1/admin/config/file/* surface
- EntityRenderer: split renderList → renderMetadataList + renderFileList; TableEntityRenderer uses url field for metadata items, name field for file-config items; SOURCE column retired
- All entity commands (model/application/toolset/interceptor/role/key/route/schema/settings/get): --source option changed from String to ConfigSource enum
- Tests: update listing mock URLs to /v1/metadata/..., response shape to {url:...}, hasMore → nextToken
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…movals - Remove --verbose global flag (dropped in 3594e4c) - Replace top-level `dial-cli diff` with per-entity `<type> diff` syntax (cross-type diff dropped alongside /v1/admin/export in 6667899) - Add --source {API|FILE} flag documentation to list/get commands (U.1, f2ca7d9) - Fix settings reset → settings delete (no reset alias in SettingsCommand) - Remove OS keystore from credential chain (not implemented; 3-step chain only) - Add fish-not-supported caveat to shell completion docs Touches: 05-cli-design.md, 06-cli-user-guide.md, 03-api-reference.md, 07-migration-and-rollout.md, IMPLEMENTATION.md, sample/dial-cli/README.md, sample/dial-cli/manifests/base/09-settings.yaml Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix settings reset → settings delete (no reset alias in SettingsCommand)
- Remove OS keystore from credential chain (not implemented)
- Fix dial-cli promote → dial-cli <type> promote (no top-level promote command)
- Add --source {API|FILE} flag to list/get rows
- Completion: fish not yet supported
- Remove OS keystore from ${SECRET:} namespace description
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add @jsonvalue toString() to ConfigSource returning lowercase so Picocli matches user input (api/file) via toString() fallback, mirroring OutputFormat. Update all --source option descriptions to use ${COMPLETION-CANDIDATES} and ${DEFAULT-VALUE}, and change defaultValue to lowercase "api" for consistency. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ecutionExceptionHandler Introduce CliException (unchecked, carries exit code) and DialExceptionHandler (picocli IExecutionExceptionHandler) so EntityWriter throws on all failure paths instead of catching exceptions and writing to stderr itself — analogous to Spring's @RestControllerAdvice. NetworkException now extends CliException(exitCode=1). DialCliCommandLineFactory registers the handler for the Quarkus production path; test run() helpers install it directly on CommandLine. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…iException Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…or handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sed CDI wiring Removes the DefaultPicocliCommandLineFactory subclass that caused an AmbiguousResolutionException at startup (two beans satisfying the PicocliCommandLineFactory injection point). Introduces CliConfiguration with a @produces CommandLine method and DialCliFactory as a plain static utility for test use. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ramValueConverter
Removes the hand-rolled parseParams/parseParamValue loop (indexOf-based key=value
splitting) in EntityWriter. picocli now owns key=value splitting via Map<String, Object>
with an explicit two-converter declaration:
converter = {UseDefaultConverter.class, ParamValueConverter.class}
UseDefaultConverter handles keys (String identity); ParamValueConverter handles values,
converting [a,b,c] bracket notation to List<String> and leaving plain strings as-is.
All nine entity-command classes and ApplyCommand updated to Map<String, Object> params
initialised to new HashMap<>() so callers always receive a non-null map without
defensive guards inside EntityWriter.
Integration tests added to TemplateResolutionTest covering: string param substitution,
multiple params, param value containing '=', and invalid param format (no '=').
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ith picocli converter
- Add JsonNodeValueConverter: ITypeConverter<JsonNode> that tries JSON parse,
falls back to TextNode — mirrors ParamValueConverter pattern
- Extract applySets logic into JsonPatcher.apply(ObjectNode, Map<String,JsonNode>);
refactor traversal to use Jackson JsonPointer + withObject() instead of manual
segment loop
- Add JsonPatcherTest covering null/empty no-ops, scalar/array/object values,
nested and deep auto-created paths, and error cases (empty segment, non-object
intermediate)
- Change --set option in all 9 Update subcommands from List<String> to
Map<String,JsonNode> with {UseDefaultConverter, JsonNodeValueConverter};
value parsing now happens at picocli parse time rather than in application code
- Remove applySets and parseSetValue from EntityWriter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
These methods either succeed or throw CliException — they never return a meaningful non-zero value. Changing to void removes the IDE warning "Method always returns 0" and makes the contract explicit. Callers now invoke the method as a statement and return 0 themselves. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… headers in CliHttpClient Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…docs Features.java has no streamingSupported field; Jackson would silently ignore it. Removed from config.yaml, 06-model.json, and both doc files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…RET:} placeholders
The server rejects a blank Key.key with 400 ("Key.key must be provided
explicitly"); auto-generation was never implemented despite OQ-12 saying
so, and --reveal-secrets does not exist (retired in U.4).
- Replace incorrect comment in 04-key.yaml; add key: "${SECRET:DIAL_CI_KEY}"
- Add key field to 04-key.json, 00-batch-array.json, and the bundle Key entry
- Correct OQ-12 to reflect that key is required, not server-generated
- Update README Caveats with the three env vars callers must export
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dot-path segments were naively joined with '/' to build a JsonPointer, so a key like 'models/public/gpt-4' was split into three segments by JsonPointer. Fix: split on dot, then escape '~'→'~0' and '/'→'~1' per RFC 6901 before compiling the pointer. Setting a path to null now removes the field instead of writing a JSON null. Adds tests for slash-in-key, tilde-in-key, both combined, and all null removal cases. Updates README and sandbox design docs to reflect the corrected dot-path syntax and null-removal behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nvelope Name, status, and validationWarnings leaked into the manifest spec sent to /v1/admin/apply, causing Jackson to fail with "Failed to parse entity at line -1, column -1". Mirror the same remove(PROJECTION_FIELDS) pattern used in updateEntity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
38e00fe to
29beb1a
Compare
--env on list/current was misleading — it could print a non-existent environment name as the active one. Both commands now read exclusively from defaults.env in the profile; resolveCurrent (which honours --env) is still used by env check where the override is intentional. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move resolveDefault/resolveCurrent from EnvCommand into EnvResolver so both the command layer and resolveEnv share one implementation. Replace the explicitEnv == null guard in resolveEnv with a named crossEnv boolean to make the api-url override intent explicit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Applicable issues
Description of changes
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.