mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-01-10 14:58:02 -05:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c7ce4a974 | ||
|
|
626c492c63 | ||
|
|
71fb3fea7e | ||
|
|
3bc1150da4 | ||
|
|
827e0aeca7 | ||
|
|
0a1e01c4ab | ||
|
|
6003bb2c86 | ||
|
|
bb896b1064 | ||
|
|
d149c62a37 | ||
|
|
3d25fbc04c | ||
|
|
4c822d2c59 | ||
|
|
f1ffd6ee29 | ||
|
|
deb59bdd21 | ||
|
|
2a1e8dcf12 | ||
|
|
b6fd81dd16 | ||
|
|
5b723c9e92 |
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,5 +1,38 @@
|
||||
# Changelog
|
||||
|
||||
## v1.4.344 (2025-12-14)
|
||||
|
||||
### PR [#1867](https://github.com/danielmiessler/Fabric/pull/1867) by [jaredmontoya](https://github.com/jaredmontoya): chore: update flake
|
||||
|
||||
- Chore: update flake
|
||||
- Merge branch 'main' into update-flake
|
||||
|
||||
## v1.4.343 (2025-12-14)
|
||||
|
||||
### PR [#1829](https://github.com/danielmiessler/Fabric/pull/1829) by [dependabo](https://github.com/apps/dependabot): chore(deps): bump js-yaml from 4.1.0 to 4.1.1 in /web in the npm_and_yarn group across 1 directory
|
||||
|
||||
- Updated js-yaml dependency from version 4.1.0 to 4.1.1 in the /web directory
|
||||
|
||||
## v1.4.342 (2025-12-13)
|
||||
|
||||
### PR [#1866](https://github.com/danielmiessler/Fabric/pull/1866) by [ksylvan](https://github.com/ksylvan): fix: write CLI and streaming errors to stderr
|
||||
|
||||
- Fix: write CLI and streaming errors to stderr
|
||||
- Route CLI execution errors to standard error output
|
||||
- Print Anthropic stream errors to stderr consistently
|
||||
- Add os import to support stderr error writes
|
||||
- Preserve help-output suppression and exit behavior
|
||||
|
||||
## v1.4.341 (2025-12-10)
|
||||
|
||||
### PR [#1860](https://github.com/danielmiessler/Fabric/pull/1860) by [ksylvan](https://github.com/ksylvan): fix: allow resetting required settings without validation errors
|
||||
|
||||
- Fix: allow resetting required settings without validation errors
|
||||
- Update `Ask` to detect reset command and bypass validation
|
||||
- Refactor `OnAnswer` to support new `isReset` parameter logic
|
||||
- Invoke `ConfigureCustom` in `Setup` to avoid redundant re-validation
|
||||
- Add unit tests ensuring required fields can be reset
|
||||
|
||||
## v1.4.340 (2025-12-08)
|
||||
|
||||
### PR [#1856](https://github.com/danielmiessler/Fabric/pull/1856) by [ksylvan](https://github.com/ksylvan): Add support for new ClaudeHaiku 4.5 models
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
func main() {
|
||||
err := cli.Cli(version)
|
||||
if err != nil && !flags.WroteHelp(err) {
|
||||
fmt.Printf("%s\n", err)
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
var version = "v1.4.340"
|
||||
var version = "v1.4.344"
|
||||
|
||||
Binary file not shown.
24
flake.lock
generated
24
flake.lock
generated
@@ -5,11 +5,11 @@
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694529238,
|
||||
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -26,11 +26,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742209644,
|
||||
"narHash": "sha256-jMy1XqXqD0/tJprEbUmKilTkvbDY/C0ZGSsJJH4TNCE=",
|
||||
"lastModified": 1763982521,
|
||||
"narHash": "sha256-ur4QIAHwgFc0vXiaxn5No/FuZicxBr2p0gmT54xZkUQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "gomod2nix",
|
||||
"rev": "8f3534eb8f6c5c3fce799376dc3b91bae6b11884",
|
||||
"rev": "02e63a239d6eabd595db56852535992c898eba72",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -41,11 +41,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1745234285,
|
||||
"narHash": "sha256-GfpyMzxwkfgRVN0cTGQSkTC0OHhEkv3Jf6Tcjm//qZ0=",
|
||||
"lastModified": 1765472234,
|
||||
"narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c11863f1e964833214b767f4a369c6e6a7aba141",
|
||||
"rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -100,11 +100,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1744961264,
|
||||
"narHash": "sha256-aRmUh0AMwcbdjJHnytg1e5h5ECcaWtIFQa6d9gI85AI=",
|
||||
"lastModified": 1762938485,
|
||||
"narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "8d404a69efe76146368885110f29a2ca3700bee6",
|
||||
"rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -216,7 +217,7 @@ func (an *Client) SendStream(
|
||||
}
|
||||
|
||||
if stream.Err() != nil {
|
||||
fmt.Printf("Messages stream error: %v\n", stream.Err())
|
||||
fmt.Fprintf(os.Stderr, "Messages stream error: %v\n", stream.Err())
|
||||
}
|
||||
close(channel)
|
||||
return
|
||||
|
||||
@@ -92,7 +92,11 @@ func (o *PluginBase) Setup() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
err = o.Configure()
|
||||
// After Setup, run ConfigureCustom if present, but skip re-validation
|
||||
// since Ask() already validated user input (or allowed explicit reset)
|
||||
if o.ConfigureCustom != nil {
|
||||
err = o.ConfigureCustom()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -198,16 +202,21 @@ func (o *SetupQuestion) Ask(label string) (err error) {
|
||||
var answer string
|
||||
fmt.Scanln(&answer)
|
||||
answer = strings.TrimRight(answer, "\n")
|
||||
isReset := strings.ToLower(answer) == AnswerReset
|
||||
if answer == "" {
|
||||
answer = o.Value
|
||||
} else if strings.ToLower(answer) == AnswerReset {
|
||||
} else if isReset {
|
||||
answer = ""
|
||||
}
|
||||
err = o.OnAnswer(answer)
|
||||
err = o.OnAnswerWithReset(answer, isReset)
|
||||
return
|
||||
}
|
||||
|
||||
func (o *SetupQuestion) OnAnswer(answer string) (err error) {
|
||||
return o.OnAnswerWithReset(answer, false)
|
||||
}
|
||||
|
||||
func (o *SetupQuestion) OnAnswerWithReset(answer string, isReset bool) (err error) {
|
||||
if o.Type == SettingTypeBool {
|
||||
if answer == "" {
|
||||
o.Value = ""
|
||||
@@ -226,6 +235,11 @@ func (o *SetupQuestion) OnAnswer(answer string) (err error) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Skip validation when explicitly resetting a value - the user intentionally
|
||||
// wants to clear the value even if it's required
|
||||
if isReset {
|
||||
return nil
|
||||
}
|
||||
err = o.IsValidErr()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -116,6 +116,91 @@ func TestSetupQuestion_Ask(t *testing.T) {
|
||||
assert.Equal(t, "user_value", setting.Value)
|
||||
}
|
||||
|
||||
func TestSetupQuestion_Ask_Reset(t *testing.T) {
|
||||
// Test that resetting a required field doesn't produce an error
|
||||
setting := &Setting{
|
||||
EnvVariable: "TEST_RESET_SETTING",
|
||||
Value: "existing_value",
|
||||
Required: true,
|
||||
}
|
||||
question := &SetupQuestion{
|
||||
Setting: setting,
|
||||
Question: "Enter test setting:",
|
||||
}
|
||||
input := "reset\n"
|
||||
fmtInput := captureInput(input)
|
||||
defer fmtInput()
|
||||
err := question.Ask("TestConfigurable")
|
||||
// Should NOT return an error even though the field is required
|
||||
assert.NoError(t, err)
|
||||
// Value should be cleared
|
||||
assert.Equal(t, "", setting.Value)
|
||||
}
|
||||
|
||||
func TestSetupQuestion_OnAnswerWithReset(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setting *Setting
|
||||
answer string
|
||||
isReset bool
|
||||
expectError bool
|
||||
expectValue string
|
||||
}{
|
||||
{
|
||||
name: "reset required field should not error",
|
||||
setting: &Setting{
|
||||
EnvVariable: "TEST_SETTING",
|
||||
Value: "old_value",
|
||||
Required: true,
|
||||
},
|
||||
answer: "",
|
||||
isReset: true,
|
||||
expectError: false,
|
||||
expectValue: "",
|
||||
},
|
||||
{
|
||||
name: "empty answer on required field should error",
|
||||
setting: &Setting{
|
||||
EnvVariable: "TEST_SETTING",
|
||||
Value: "",
|
||||
Required: true,
|
||||
},
|
||||
answer: "",
|
||||
isReset: false,
|
||||
expectError: true,
|
||||
expectValue: "",
|
||||
},
|
||||
{
|
||||
name: "valid answer on required field should not error",
|
||||
setting: &Setting{
|
||||
EnvVariable: "TEST_SETTING",
|
||||
Value: "",
|
||||
Required: true,
|
||||
},
|
||||
answer: "new_value",
|
||||
isReset: false,
|
||||
expectError: false,
|
||||
expectValue: "new_value",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
question := &SetupQuestion{
|
||||
Setting: tt.setting,
|
||||
Question: "Test question",
|
||||
}
|
||||
err := question.OnAnswerWithReset(tt.answer, tt.isReset)
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, tt.expectValue, tt.setting.Value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettings_IsConfigured(t *testing.T) {
|
||||
settings := Settings{
|
||||
{EnvVariable: "TEST_SETTING1", Value: "value1", Required: true},
|
||||
|
||||
@@ -1 +1 @@
|
||||
"1.4.340"
|
||||
"1.4.344"
|
||||
|
||||
16
web/pnpm-lock.yaml
generated
16
web/pnpm-lock.yaml
generated
@@ -323,8 +323,8 @@ packages:
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
||||
|
||||
'@eslint-community/regexpp@4.12.1':
|
||||
resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
|
||||
'@eslint-community/regexpp@4.12.2':
|
||||
resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
|
||||
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||
|
||||
'@eslint/config-array@0.19.2':
|
||||
@@ -1387,8 +1387,8 @@ packages:
|
||||
resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
|
||||
hasBin: true
|
||||
|
||||
js-yaml@4.1.0:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
jsbn@0.1.1:
|
||||
@@ -2484,7 +2484,7 @@ snapshots:
|
||||
eslint: 9.17.0(jiti@1.21.7)
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/regexpp@4.12.1': {}
|
||||
'@eslint-community/regexpp@4.12.2': {}
|
||||
|
||||
'@eslint/config-array@0.19.2':
|
||||
dependencies:
|
||||
@@ -2510,7 +2510,7 @@ snapshots:
|
||||
globals: 14.0.0
|
||||
ignore: 5.3.2
|
||||
import-fresh: 3.3.1
|
||||
js-yaml: 4.1.0
|
||||
js-yaml: 4.1.1
|
||||
minimatch: 3.1.2
|
||||
strip-json-comments: 3.1.1
|
||||
transitivePeerDependencies:
|
||||
@@ -3208,7 +3208,7 @@ snapshots:
|
||||
eslint@9.17.0(jiti@1.21.7):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.0(eslint@9.17.0(jiti@1.21.7))
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
'@eslint/config-array': 0.19.2
|
||||
'@eslint/core': 0.9.1
|
||||
'@eslint/eslintrc': 3.3.1
|
||||
@@ -3604,7 +3604,7 @@ snapshots:
|
||||
|
||||
jiti@1.21.7: {}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user