Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## v1.4.477 (2026-09-03)

### PR [#2211](https://github.com/danielmiessler/Fabric/pull/2211) by [ksylvan](https://github.com/ksylvan): fix: prevent pattern loader temporary directory leaks

- Prevent pattern loader leaks by lazily creating temporary directories during database population and cleaning them up after successful or failed downloads.
- Add regression tests for lazy directory creation and cleanup.

## v1.4.476 (2026-09-03)

### PR [#2210](https://github.com/danielmiessler/Fabric/pull/2210) by [ksylvan](https://github.com/ksylvan): feat: add Pzero as an OpenAI-compatible AI provider

- Added Pzero as an OpenAI-compatible AI provider.
- Registered Pzero with its OpenAI-compatible API base URL.
- Added Pzero to the README鈥檚 list of supported AI providers.

## v1.4.475 (2026-09-03)

### PR [#2209](https://github.com/danielmiessler/Fabric/pull/2209) by [kadiryildiz283](https://github.com/kadiryildiz283): feat(i18n): add Turkish (tr) translation
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ Fabric supports a wide range of AI providers:
- Mistral
- Novita AI
- OpenRouter
- Pzero
- SiliconCloud
- Synthorai
- Together
Expand Down
2 changes: 1 addition & 1 deletion cmd/fabric/version.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package main

var version = "v1.4.475"
var version = "v1.4.477"
Binary file modified cmd/generate_changelog/changelog.db
Binary file not shown.
5 changes: 5 additions & 0 deletions internal/plugins/ai/openai_compatible/providers_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,11 @@ var ProviderMap = map[string]ProviderConfig{
BaseURL: "https://openrouter.ai/api/v1",
ImplementsResponses: false,
},
"Pzero": {
Name: "Pzero",
BaseURL: "https://api.pzero.studio/v1",
ImplementsResponses: false,
},
"SiliconCloud": {
Name: "SiliconCloud",
BaseURL: "https://api.siliconflow.cn/v1",
Expand Down
16 changes: 9 additions & 7 deletions internal/tools/patterns_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,6 @@ type PatternsLoader struct {

func (o *PatternsLoader) configure() (err error) {
o.pathPatternsPrefix = fmt.Sprintf("%v/", o.DefaultFolder.Value)
// Use a consistent temp folder name regardless of the source path structure
tempDir, err := os.MkdirTemp("", "fabric-patterns-")
if err != nil {
return fmt.Errorf(i18n.T("patterns_failed_create_temp_folder"), err)
}
o.tempPatternsFolder = tempDir

return
}

Expand Down Expand Up @@ -96,6 +89,15 @@ func (o *PatternsLoader) PopulateDB() (err error) {
fmt.Println()
fmt.Println()

// Create the temp folder here, not in configure(), so invocations that
// do not download patterns do not leak an empty directory (issue #2190).
var tempDir string
if tempDir, err = os.MkdirTemp("", "fabric-patterns-"); err != nil {
return fmt.Errorf(i18n.T("patterns_failed_create_temp_folder"), err)
}
o.tempPatternsFolder = tempDir
defer os.RemoveAll(tempDir)

originalPath := o.DefaultFolder.Value
if err = o.gitCloneAndCopy(); err != nil {
return fmt.Errorf(i18n.T("patterns_failed_download_from_git"), err)
Expand Down
63 changes: 63 additions & 0 deletions internal/tools/patterns_loader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package tools

import (
"os"
"path/filepath"
"testing"

"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
)

// Configure runs on every fabric invocation via the plugin registry. It must
// not create the patterns temp directory; only PopulateDB uses it.
func TestConfigureDoesNotCreateTempDir(t *testing.T) {
tmp := t.TempDir()
t.Setenv("TMPDIR", tmp)
t.Setenv("TMP", tmp)
t.Setenv("TEMP", tmp)

loader := NewPatternsLoader(&fsdb.PatternsEntity{
StorageEntity: &fsdb.StorageEntity{Dir: t.TempDir()},
})
if err := loader.Configure(); err != nil {
t.Fatalf("Configure() failed: %v", err)
}

matches, err := filepath.Glob(filepath.Join(tmp, "fabric-patterns-*"))
if err != nil {
t.Fatal(err)
}
if len(matches) != 0 {
t.Errorf("Configure() created temp directories: %v", matches)
}
}

// PopulateDB must create the temp directory lazily and remove it when done,
// even on failure.
func TestPopulateDBCleansUpTempDir(t *testing.T) {
tmp := t.TempDir()
t.Setenv("TMPDIR", tmp)
t.Setenv("TMP", tmp)
t.Setenv("TEMP", tmp)

loader := NewPatternsLoader(&fsdb.PatternsEntity{
StorageEntity: &fsdb.StorageEntity{Dir: t.TempDir()},
})
if err := loader.Configure(); err != nil {
t.Fatalf("Configure() failed: %v", err)
}
// Point at an invalid repo so PopulateDB fails fast without network.
loader.DefaultGitRepoUrl.Value = filepath.Join(t.TempDir(), "no-such-repo")

if err := loader.PopulateDB(); err == nil {
t.Fatal("PopulateDB() unexpectedly succeeded with invalid repo")
}

entries, err := os.ReadDir(tmp)
if err != nil {
t.Fatal(err)
}
for _, e := range entries {
t.Errorf("PopulateDB() left temp entry behind: %v", e.Name())
}
}
2 changes: 1 addition & 1 deletion nix/pkgs/fabric/version.nix
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"1.4.475"
"1.4.477"
Loading