@pi-in-go/pigpen-acp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CREDITS.md +23 -0
- package/LICENSE +22 -0
- package/README.md +110 -0
- package/extensions/acp/cmd/pig-acp/e2e_llm_test.go +168 -0
- package/extensions/acp/cmd/pig-acp/e2e_test.go +541 -0
- package/extensions/acp/cmd/pig-acp/go.mod +6 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/agent.go +784 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/agent_builtin_test.go +536 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/agent_commands_test.go +199 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/agent_config_test.go +530 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/agent_helpers_test.go +215 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/agent_load_test.go +333 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/agent_prompt.go +289 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/agent_session_test.go +429 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/auth.go +61 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/bench_test.go +47 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/helpers_test.go +421 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/main_test.go +28 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/mutation_extra_test.go +142 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/paths.go +77 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/pisessions.go +317 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/protocol.go +188 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/server.go +218 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/server_test.go +386 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/session.go +1068 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/session_diff_test.go +183 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/session_events_test.go +443 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/session_extra_test.go +547 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/session_usage_test.go +198 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/settings.go +79 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/slash.go +278 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/slash_test.go +128 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/startup.go +156 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/store.go +118 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/store_paths_test.go +466 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/translate.go +352 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/translate_test.go +105 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/twin_test.go +28 -0
- package/extensions/acp/cmd/pig-acp/internal/acp/types.go +202 -0
- package/extensions/acp/cmd/pig-acp/internal/jsonrpc/bench_test.go +24 -0
- package/extensions/acp/cmd/pig-acp/internal/jsonrpc/jsonrpc.go +302 -0
- package/extensions/acp/cmd/pig-acp/internal/jsonrpc/jsonrpc_test.go +320 -0
- package/extensions/acp/cmd/pig-acp/internal/jsonrpc/twin_test.go +28 -0
- package/extensions/acp/cmd/pig-acp/internal/pirpc/kill_unix.go +19 -0
- package/extensions/acp/cmd/pig-acp/internal/pirpc/kill_windows.go +11 -0
- package/extensions/acp/cmd/pig-acp/internal/pirpc/process.go +530 -0
- package/extensions/acp/cmd/pig-acp/internal/pirpc/process_test.go +355 -0
- package/extensions/acp/cmd/pig-acp/internal/pirpc/twin_test.go +28 -0
- package/extensions/acp/cmd/pig-acp/main.go +177 -0
- package/extensions/acp/cmd/pig-acp/main_test.go +213 -0
- package/extensions/acp/cmd/pig-acp/sigpipe_unix.go +10 -0
- package/extensions/acp/cmd/pig-acp/sigpipe_windows.go +5 -0
- package/extensions/acp/cmd/pig-acp/testdata/ask/extension.go +28 -0
- package/extensions/acp/cmd/pig-acp/testdata/ask/go.mod +7 -0
- package/extensions/acp/cmd/pig-acp/testdata/ask/go.sum +2 -0
- package/extensions/acp/cmd/pig-acp/testdata/gate/extension.go +37 -0
- package/extensions/acp/cmd/pig-acp/testdata/gate/go.mod +7 -0
- package/extensions/acp/cmd/pig-acp/testdata/gate/go.sum +2 -0
- package/extensions/acp/cmd/pig-acp/twin_test.go +28 -0
- package/extensions/acp/extension.go +63 -0
- package/extensions/acp/extension_test.go +111 -0
- package/extensions/acp/fakehost_test.go +548 -0
- package/extensions/acp/go.mod +6 -0
- package/extensions/acp/go.sum +2 -0
- package/package.json +41 -0
- package/port/PORT.md +147 -0
- package/port/interop/package-lock.json +89 -0
- package/port/interop/package.json +13 -0
- package/port/interop/run.mjs +229 -0
- package/port/mutate.mjs +110 -0
- package/port/mutation-results.txt +107 -0
- package/port/mutations.json +748 -0
- package/port/oracle/pi-acp/LICENSE +21 -0
- package/port/oracle/pi-acp/README.md +209 -0
- package/port/oracle/pi-acp/package.json +65 -0
- package/port/oracle/pi-acp/src/acp/agent.ts +1661 -0
- package/port/oracle/pi-acp/src/acp/auth-required.ts +37 -0
- package/port/oracle/pi-acp/src/acp/auth.ts +59 -0
- package/port/oracle/pi-acp/src/acp/paths.ts +15 -0
- package/port/oracle/pi-acp/src/acp/pi-commands.ts +59 -0
- package/port/oracle/pi-acp/src/acp/pi-sessions.ts +333 -0
- package/port/oracle/pi-acp/src/acp/pi-settings.ts +75 -0
- package/port/oracle/pi-acp/src/acp/session-store.ts +68 -0
- package/port/oracle/pi-acp/src/acp/session.ts +1078 -0
- package/port/oracle/pi-acp/src/acp/slash-commands.ts +197 -0
- package/port/oracle/pi-acp/src/acp/translate/bash.ts +100 -0
- package/port/oracle/pi-acp/src/acp/translate/pi-messages.ts +17 -0
- package/port/oracle/pi-acp/src/acp/translate/pi-tools.ts +51 -0
- package/port/oracle/pi-acp/src/acp/translate/prompt.ts +71 -0
- package/port/oracle/pi-acp/src/index.ts +82 -0
- package/port/oracle/pi-acp/src/pi-rpc/command.ts +16 -0
- package/port/oracle/pi-acp/src/pi-rpc/process.ts +432 -0
- package/port/oracle/pi-acp/test/component/agent-steering-followup-modes.test.ts +143 -0
- package/port/oracle/pi-acp/test/component/session-diff.test.ts +214 -0
- package/port/oracle/pi-acp/test/component/session-events.test.ts +1102 -0
- package/port/oracle/pi-acp/test/component/session-list-and-load.test.ts +129 -0
- package/port/oracle/pi-acp/test/component/session-list-custom-session-dir.test.ts +52 -0
- package/port/oracle/pi-acp/test/component/session-list-scoped.test.ts +76 -0
- package/port/oracle/pi-acp/test/component/session-load-toolresult.test.ts +68 -0
- package/port/oracle/pi-acp/test/component/session-queue-cancel.test.ts +43 -0
- package/port/oracle/pi-acp/test/component/session-slash-commands.test.ts +37 -0
- package/port/oracle/pi-acp/test/component/session-thinking-modes.test.ts +22 -0
- package/port/oracle/pi-acp/test/component/session-title-long-session.test.ts +56 -0
- package/port/oracle/pi-acp/test/component/session-updatedAt-message-only.test.ts +57 -0
- package/port/oracle/pi-acp/test/helpers/fakes.ts +87 -0
- package/port/oracle/pi-acp/test/unit/auth-methods-terminal-auth-meta.test.ts +22 -0
- package/port/oracle/pi-acp/test/unit/builtin-commands.test.ts +60 -0
- package/port/oracle/pi-acp/test/unit/context-usage.test.ts +204 -0
- package/port/oracle/pi-acp/test/unit/merge-commands.test.ts +19 -0
- package/port/oracle/pi-acp/test/unit/model-thinking-levels.test.ts +268 -0
- package/port/oracle/pi-acp/test/unit/new-session-auth-required-when-no-models.test.ts +51 -0
- package/port/oracle/pi-acp/test/unit/new-session-pi-not-found.test.ts +26 -0
- package/port/oracle/pi-acp/test/unit/new-session-runtime-startup-errors.test.ts +148 -0
- package/port/oracle/pi-acp/test/unit/pi-command.test.ts +44 -0
- package/port/oracle/pi-acp/test/unit/pi-commands.test.ts +32 -0
- package/port/oracle/pi-acp/test/unit/pi-enable-embed-context-flag.test.ts +35 -0
- package/port/oracle/pi-acp/test/unit/pi-messages.test.ts +29 -0
- package/port/oracle/pi-acp/test/unit/pi-rpc-request-timeout.test.ts +137 -0
- package/port/oracle/pi-acp/test/unit/pi-rpc-session-path.test.ts +93 -0
- package/port/oracle/pi-acp/test/unit/pi-tools.test.ts +40 -0
- package/port/oracle/pi-acp/test/unit/prompt-to-pi-message.test.ts +74 -0
- package/port/oracle/pi-acp/test/unit/session-config-options.test.ts +217 -0
- package/port/oracle/pi-acp/test/unit/session-delete.test.ts +170 -0
- package/port/oracle/pi-acp/test/unit/session-restore.test.ts +239 -0
- package/port/oracle/pi-acp/test/unit/slash-commands.test.ts +36 -0
- package/port/oracle/pi-acp/test/unit/startup-info-env.test.ts +84 -0
- package/port/oracle/pi-acp/test/unit/startup-info-load-session.test.ts +53 -0
- package/port/oracle/pi-acp/test/unit/startup-info-project-packages.test.ts +74 -0
- package/port/oracle/pi-acp/test/unit/stdout-destroyed-does-not-crash.test.ts +32 -0
- package/port/oracle/pi-acp/test/unit/thinking-level-rpc.test.ts +43 -0
- package/port/schema/LICENSE +191 -0
- package/port/schema/schema.json +8015 -0
- package/port/upstream-tests.json +201 -0
- package/provenance.json +28 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
package acp
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"os"
|
|
6
|
+
"os/exec"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"regexp"
|
|
9
|
+
"sort"
|
|
10
|
+
"strings"
|
|
11
|
+
"time"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
var leadingV = regexp.MustCompile(`^[vV]`)
|
|
15
|
+
|
|
16
|
+
// pigVersionHeader is `pig --version`, or "" when the executable does not answer.
|
|
17
|
+
func pigVersionHeader(piCommand string) string {
|
|
18
|
+
if piCommand == "" {
|
|
19
|
+
piCommand = "pig"
|
|
20
|
+
}
|
|
21
|
+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
22
|
+
defer cancel()
|
|
23
|
+
cmd := exec.CommandContext(ctx, piCommand, "--version")
|
|
24
|
+
out, err := cmd.Output()
|
|
25
|
+
text := jsTrim(string(out))
|
|
26
|
+
if text == "" && err != nil {
|
|
27
|
+
return ""
|
|
28
|
+
}
|
|
29
|
+
if text == "" {
|
|
30
|
+
if ee, ok := err.(*exec.ExitError); ok {
|
|
31
|
+
text = jsTrim(string(ee.Stderr))
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return leadingV.ReplaceAllString(text, "")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
func readDirNames(dir string) []string {
|
|
38
|
+
entries, err := os.ReadDir(dir)
|
|
39
|
+
if err != nil {
|
|
40
|
+
return nil
|
|
41
|
+
}
|
|
42
|
+
names := make([]string, 0, len(entries))
|
|
43
|
+
for _, e := range entries {
|
|
44
|
+
names = append(names, e.Name())
|
|
45
|
+
}
|
|
46
|
+
sort.Strings(names)
|
|
47
|
+
return names
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func isDirPath(p string) (isDir, isFile, ok bool) {
|
|
51
|
+
st, err := os.Stat(p)
|
|
52
|
+
if err != nil {
|
|
53
|
+
return false, false, false
|
|
54
|
+
}
|
|
55
|
+
return st.IsDir(), st.Mode().IsRegular(), true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func skillsFromRoot(root string) []string {
|
|
59
|
+
var items []string
|
|
60
|
+
for _, e := range readDirNames(root) {
|
|
61
|
+
p := filepath.Join(root, e)
|
|
62
|
+
if _, isFile, ok := isDirPath(p); ok && isFile && strings.HasSuffix(strings.ToLower(e), ".md") {
|
|
63
|
+
items = append(items, p)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
stack := []string{root}
|
|
67
|
+
for len(stack) > 0 {
|
|
68
|
+
dir := stack[len(stack)-1]
|
|
69
|
+
stack = stack[:len(stack)-1]
|
|
70
|
+
for _, name := range readDirNames(dir) {
|
|
71
|
+
if name == "node_modules" || name == ".git" {
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
p := filepath.Join(dir, name)
|
|
75
|
+
isDir, isFile, ok := isDirPath(p)
|
|
76
|
+
if !ok {
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
if isDir {
|
|
80
|
+
stack = append(stack, p)
|
|
81
|
+
} else if isFile && name == "SKILL.md" {
|
|
82
|
+
items = append(items, p)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return items
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// BuildStartupInfo builds the markdown "startup info" block (the original's buildStartupInfo,
|
|
90
|
+
// without its npm update notice: see PORT.md, gap A18).
|
|
91
|
+
func BuildStartupInfo(cwd, piCommand string) string {
|
|
92
|
+
var md []string
|
|
93
|
+
if v := pigVersionHeader(piCommand); v != "" {
|
|
94
|
+
md = append(md, "pig v"+v, "---", "")
|
|
95
|
+
}
|
|
96
|
+
addSection := func(title string, items []string) {
|
|
97
|
+
var cleaned []string
|
|
98
|
+
for _, s := range items {
|
|
99
|
+
if s = jsTrim(s); s != "" {
|
|
100
|
+
cleaned = append(cleaned, s)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if len(cleaned) == 0 {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
md = append(md, "## "+title)
|
|
107
|
+
for _, item := range cleaned {
|
|
108
|
+
md = append(md, "- "+item)
|
|
109
|
+
}
|
|
110
|
+
md = append(md, "")
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
var context []string
|
|
114
|
+
contextPath := filepath.Join(cwd, "AGENTS.md")
|
|
115
|
+
if _, err := os.Stat(contextPath); err == nil {
|
|
116
|
+
context = append(context, contextPath)
|
|
117
|
+
}
|
|
118
|
+
addSection("Context", context)
|
|
119
|
+
|
|
120
|
+
var skills []string
|
|
121
|
+
skills = append(skills, skillsFromRoot(filepath.Join(AgentDir(), "skills"))...)
|
|
122
|
+
skills = append(skills, skillsFromRoot(filepath.Join(userHome(), ".agents", "skills"))...)
|
|
123
|
+
skills = append(skills, skillsFromRoot(filepath.Join(cwd, ProjectDirName(), "skills"))...)
|
|
124
|
+
addSection("Skills", skills)
|
|
125
|
+
|
|
126
|
+
var prompts []string
|
|
127
|
+
for _, f := range readDirNames(filepath.Join(AgentDir(), "prompts")) {
|
|
128
|
+
if strings.HasSuffix(f, ".md") {
|
|
129
|
+
prompts = append(prompts, "/"+strings.TrimSuffix(f, ".md"))
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
addSection("Prompts", prompts)
|
|
133
|
+
|
|
134
|
+
var exts []string
|
|
135
|
+
extDir := filepath.Join(AgentDir(), "extensions")
|
|
136
|
+
for _, f := range readDirNames(extDir) {
|
|
137
|
+
if strings.HasSuffix(f, ".ts") || strings.HasSuffix(f, ".js") {
|
|
138
|
+
exts = append(exts, filepath.Join(extDir, f))
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for _, sp := range []string{filepath.Join(AgentDir(), "settings.json"), filepath.Join(cwd, ProjectDirName(), "settings.json")} {
|
|
142
|
+
settings := readJSONObject(sp)
|
|
143
|
+
pkgs, _ := settings["packages"].([]any)
|
|
144
|
+
for _, p := range pkgs {
|
|
145
|
+
s := jsString(p)
|
|
146
|
+
if strings.HasPrefix(s, "npm:") {
|
|
147
|
+
exts = append(exts, s+"\n - index.ts")
|
|
148
|
+
} else {
|
|
149
|
+
exts = append(exts, s)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
addSection("Extensions", exts)
|
|
154
|
+
|
|
155
|
+
return jsTrim(strings.Join(md, "\n")) + "\n"
|
|
156
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
package acp
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"sync"
|
|
8
|
+
"time"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// StoredSession is one entry of the adapter's session map.
|
|
12
|
+
type StoredSession struct {
|
|
13
|
+
SessionID string `json:"sessionId"`
|
|
14
|
+
Cwd string `json:"cwd"`
|
|
15
|
+
SessionFile string `json:"sessionFile"`
|
|
16
|
+
UpdatedAt string `json:"updatedAt"`
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Store maps ACP session ids to pi session files (SessionStore of the original).
|
|
20
|
+
type Store interface {
|
|
21
|
+
Get(sessionID string) *StoredSession
|
|
22
|
+
Upsert(entry StoredSession)
|
|
23
|
+
Delete(sessionID string)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type sessionMapFile struct {
|
|
27
|
+
Version int `json:"version"`
|
|
28
|
+
Sessions map[string]StoredSession `json:"sessions"`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// FileStore is a Store kept in one JSON file. Every operation reads the file again, so two
|
|
32
|
+
// adapter processes (two editor windows) see each other's entries.
|
|
33
|
+
type FileStore struct {
|
|
34
|
+
path string
|
|
35
|
+
mu sync.Mutex
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// NewFileStore returns a store at path ("" = the default location under PIG_HOME).
|
|
39
|
+
func NewFileStore(path string) *FileStore {
|
|
40
|
+
if path == "" {
|
|
41
|
+
path = SessionMapPath()
|
|
42
|
+
}
|
|
43
|
+
return &FileStore{path: path}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func (s *FileStore) load() sessionMapFile {
|
|
47
|
+
empty := sessionMapFile{Version: 1, Sessions: map[string]StoredSession{}}
|
|
48
|
+
raw, err := os.ReadFile(s.path)
|
|
49
|
+
if err != nil {
|
|
50
|
+
return empty
|
|
51
|
+
}
|
|
52
|
+
var f struct {
|
|
53
|
+
Version int `json:"version"`
|
|
54
|
+
Sessions map[string]json.RawMessage `json:"sessions"`
|
|
55
|
+
}
|
|
56
|
+
if json.Unmarshal(raw, &f) != nil || f.Version != 1 || f.Sessions == nil {
|
|
57
|
+
return empty
|
|
58
|
+
}
|
|
59
|
+
for id, r := range f.Sessions {
|
|
60
|
+
var e StoredSession
|
|
61
|
+
if json.Unmarshal(r, &e) == nil {
|
|
62
|
+
empty.Sessions[id] = e
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return empty
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func (s *FileStore) save(f sessionMapFile) {
|
|
69
|
+
if err := os.MkdirAll(filepath.Dir(s.path), 0o755); err != nil {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
b, err := json.MarshalIndent(f, "", " ")
|
|
73
|
+
if err != nil {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
// Write to a sibling and rename, so a second adapter never reads half a file.
|
|
77
|
+
tmp := s.path + ".tmp"
|
|
78
|
+
if os.WriteFile(tmp, append(b, '\n'), 0o600) == nil {
|
|
79
|
+
if os.Rename(tmp, s.path) != nil {
|
|
80
|
+
_ = os.WriteFile(s.path, append(b, '\n'), 0o600)
|
|
81
|
+
_ = os.Remove(tmp)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Get returns the entry for sessionID, or nil.
|
|
87
|
+
func (s *FileStore) Get(sessionID string) *StoredSession {
|
|
88
|
+
s.mu.Lock()
|
|
89
|
+
defer s.mu.Unlock()
|
|
90
|
+
if e, ok := s.load().Sessions[sessionID]; ok {
|
|
91
|
+
return &e
|
|
92
|
+
}
|
|
93
|
+
return nil
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Upsert records or refreshes an entry.
|
|
97
|
+
func (s *FileStore) Upsert(entry StoredSession) {
|
|
98
|
+
s.mu.Lock()
|
|
99
|
+
defer s.mu.Unlock()
|
|
100
|
+
f := s.load()
|
|
101
|
+
f.Sessions[entry.SessionID] = StoredSession{
|
|
102
|
+
SessionID: entry.SessionID, Cwd: entry.Cwd, SessionFile: entry.SessionFile,
|
|
103
|
+
UpdatedAt: time.Now().UTC().Format("2006-01-02T15:04:05.000Z"),
|
|
104
|
+
}
|
|
105
|
+
s.save(f)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Delete removes an entry.
|
|
109
|
+
func (s *FileStore) Delete(sessionID string) {
|
|
110
|
+
s.mu.Lock()
|
|
111
|
+
defer s.mu.Unlock()
|
|
112
|
+
f := s.load()
|
|
113
|
+
if _, ok := f.Sessions[sessionID]; !ok {
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
delete(f.Sessions, sessionID)
|
|
117
|
+
s.save(f)
|
|
118
|
+
}
|