( store: CliServerConnectionStore, )
| 143 | }); |
| 144 | |
| 145 | export const writeCliServerConnectionStore = ( |
| 146 | store: CliServerConnectionStore, |
| 147 | ): Effect.Effect<void, PlatformError, FileSystem.FileSystem | Path.Path> => |
| 148 | Effect.gen(function* () { |
| 149 | const fs = yield* FileSystem.FileSystem; |
| 150 | const path = yield* Path.Path; |
| 151 | const dataDir = resolveDataDir(path); |
| 152 | yield* fs.makeDirectory(dataDir, { recursive: true }); |
| 153 | const storePath = serverConnectionStorePath(path); |
| 154 | // This store holds live credentials for a hosted server — a bearer token, or |
| 155 | // an OAuth access token AND its long-lived refresh token — so create it |
| 156 | // owner-only, exactly as the local-server manifest does for the sibling |
| 157 | // secret it keeps under `server-control/`. |
| 158 | // |
| 159 | // Both steps are needed, and the second matters more here than it does |
| 160 | // there. `mode` applies only when the file is CREATED, so it closes the |
| 161 | // window where a fresh store is briefly world-readable. The `chmod` covers |
| 162 | // overwriting a store that already exists with looser permissions — and this |
| 163 | // file is rewritten on every silent token refresh, so overwrite is the |
| 164 | // common path, not the rare one. |
| 165 | yield* fs.writeFileString(storePath, serializeCliServerConnectionStore(store), { |
| 166 | mode: 0o600, |
| 167 | }); |
| 168 | yield* fs.chmod(storePath, 0o600).pipe(Effect.ignore); |
| 169 | }); |
| 170 | |
| 171 | export const upsertCliServerConnectionProfile = (input: { |
| 172 | readonly name: string; |
no test coverage detected