MCPcopy Create free account
hub / github.com/anomalyco/opencode / fileSystem

Function fileSystem

packages/http-recorder/src/cassette.ts:72–145  ·  view source on GitHub ↗
(
  options: { readonly directory?: string } = {},
)

Source from the content-addressed store, hash-verified

70 findings.length === 0 ? Effect.void : Effect.fail(new UnsafeCassetteError({ cassetteName: name, findings }))
71
72export const fileSystem = (
73 options: { readonly directory?: string } = {},
74): Layer.Layer<Service, never, FileSystem.FileSystem> =>
75 Layer.effect(
76 Service,
77 Effect.gen(function* () {
78 const fs = yield* FileSystem.FileSystem
79 const directory = options.directory ?? DEFAULT_RECORDINGS_DIR
80 const recorded = new Map<string, { interactions: Interaction[]; findings: SecretFinding[] }>()
81 const appendLock = yield* Semaphore.make(1)
82
83 const pathFor = (name: string) => cassettePath(directory, name)
84
85 const walk = (current: string): Effect.Effect<ReadonlyArray<string>> =>
86 Effect.gen(function* () {
87 const entries = yield* fs.readDirectory(current).pipe(Effect.catch(() => Effect.succeed([] as string[])))
88 const nested = yield* Effect.forEach(entries, (entry) => {
89 const full = path.join(current, entry)
90 return fs.stat(full).pipe(
91 Effect.flatMap((stat) => (stat.type === "Directory" ? walk(full) : Effect.succeed([full]))),
92 Effect.catch(() => Effect.succeed([] as string[])),
93 )
94 })
95 return nested.flat()
96 })
97
98 return Service.of({
99 read: (name) =>
100 fs.readFileString(pathFor(name)).pipe(
101 Effect.map((raw) => parseCassette(raw).interactions),
102 Effect.catch(() => Effect.fail(new CassetteNotFoundError({ cassetteName: name }))),
103 ),
104 append: (name, interaction, metadata) =>
105 appendLock.withPermit(
106 Effect.gen(function* () {
107 const entry = recorded.get(name) ?? { interactions: [], findings: [] }
108 const interactions = [...entry.interactions, interaction]
109 const interactionFindings = [...entry.findings, ...secretFindings(interaction)]
110 const cassette = buildCassette(name, interactions, metadata)
111 const findings = [...interactionFindings, ...secretFindings(cassette.metadata ?? {})]
112 yield* failIfUnsafe(name, findings)
113 const target = pathFor(name)
114 yield* fs.makeDirectory(path.dirname(target), { recursive: true }).pipe(Effect.orDie)
115 const temporary = `${target}.${crypto.randomUUID()}.tmp`
116 yield* fs.writeFileString(temporary, formatCassette(cassette)).pipe(
117 Effect.flatMap(() => fs.rename(temporary, target)),
118 Effect.ensuring(fs.remove(temporary, { force: true }).pipe(Effect.catch(() => Effect.void))),
119 Effect.orDie,
120 )
121 recorded.set(name, { interactions, findings: interactionFindings })
122 }),
123 ),
124 exists: (name) =>
125 fs.access(pathFor(name)).pipe(
126 Effect.as(true),
127 Effect.catch(() => Effect.succeed(false)),
128 ),
129 list: () =>

Callers

nothing calls this directly

Calls 12

secretFindingsFunction · 0.85
buildCassetteFunction · 0.85
failIfUnsafeFunction · 0.85
formatCassetteFunction · 0.85
renameMethod · 0.80
asMethod · 0.80
pathForFunction · 0.70
walkFunction · 0.70
getMethod · 0.65
removeMethod · 0.65
makeMethod · 0.45
setMethod · 0.45

Tested by

no test coverage detected