@wirecat/cli-core 0.427.0 → 0.428.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/CHANGELOG.md +12 -0
- package/dist/cli/messenger/doctor-command.js +26 -7
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ shape of this file.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## 0.428.0 — 25.10.2026
|
|
11
|
+
|
|
12
|
+
### Changed — may break callers
|
|
13
|
+
|
|
14
|
+
- **cli-core needs `@wirecat/cli-base` 0.57.** The peer range moves from `>=0.56.0 <0.57.0` to `>=0.57.0 <0.58.0`:
|
|
15
|
+
cli-base 0.57 saves `config.json` owner-only (`0600`), which the new `doctor` check below expects. A CLI that pins
|
|
16
|
+
cli-base moves its pin with this release.
|
|
17
|
+
|
|
18
|
+
### Security
|
|
19
|
+
|
|
20
|
+
- **`doctor` reports a `config.json` or config folder that other users can read, with the `chmod` that fixes it.** The first config file is created `0600` in a `0700` folder, but cli-base up to 0.56 rewrote it as `0644` on the first `config set`, and older versions created the folder without `0700`, so on a machine used for a while both are often open. `files.problems` now lists the config file (wants `0600`) and its folder (wants `0700`), and stderr prints `to make it private: chmod 600 '<path>'`; nothing is changed on disk. The folder is checked even when `<APP>_CONFIG_DIR` names it, because the credential file falls back to it. Unlike before, `files` is also in the answer with no profile named and when the settings are refused. Windows is not checked, as before. Until cli-core moves to the cli-base release that saves the config `0600`, `config set` makes the file `0644` again and `doctor` will keep naming it.
|
|
21
|
+
|
|
10
22
|
## 0.427.0 — 25.10.2026
|
|
11
23
|
|
|
12
24
|
### Added
|
|
@@ -137,13 +137,16 @@ const diagnose = async (command, messenger) => {
|
|
|
137
137
|
catch (error) {
|
|
138
138
|
// Not the file: a profile lock, a flag or an environment variable the settings refuse.
|
|
139
139
|
const path = configFilePath(resolvePaths({ appName: app.appName, prefix: app.envPrefix, env }).config);
|
|
140
|
+
const files = privateFiles(configFiles(path));
|
|
140
141
|
return {
|
|
142
|
+
fixes: fixesOf(files),
|
|
141
143
|
report: {
|
|
142
144
|
cli,
|
|
143
145
|
config: { path, found: existsSync(path), issues: [] },
|
|
144
146
|
settings: { error: messageOf(error) },
|
|
145
147
|
store: snakeKeys(await storeSummary(env)),
|
|
146
148
|
runs: { directory: runsDirFor(app, env), kept: listRuns(runsDirFor(app, env)).length },
|
|
149
|
+
files: filesReport(files),
|
|
147
150
|
},
|
|
148
151
|
};
|
|
149
152
|
}
|
|
@@ -156,9 +159,12 @@ const diagnose = async (command, messenger) => {
|
|
|
156
159
|
...unknownPermissionIssues(command, settings, app.command, messenger.permissionKey),
|
|
157
160
|
],
|
|
158
161
|
};
|
|
159
|
-
|
|
162
|
+
const own = configFiles(settings.configPath);
|
|
163
|
+
if (settings.sources.profile === NONE_NAMED) {
|
|
164
|
+
const files = privateFiles(own);
|
|
160
165
|
return {
|
|
161
166
|
renderer,
|
|
167
|
+
fixes: fixesOf(files),
|
|
162
168
|
report: {
|
|
163
169
|
cli,
|
|
164
170
|
profile: null,
|
|
@@ -166,22 +172,30 @@ const diagnose = async (command, messenger) => {
|
|
|
166
172
|
store: snakeKeys(await storeSummary(env)),
|
|
167
173
|
readers: engineStates(),
|
|
168
174
|
runs: { directory: runsDirFor(app, env), kept: listRuns(runsDirFor(app, env)).length },
|
|
175
|
+
files: filesReport(files),
|
|
169
176
|
},
|
|
170
177
|
};
|
|
178
|
+
}
|
|
171
179
|
const { profile } = settings;
|
|
172
180
|
const account = recalledAccount(app, provider, profile, env);
|
|
173
181
|
const store = storePath(env);
|
|
174
182
|
const sends = sendsPathFor(app, profile, env);
|
|
175
183
|
const flood = floodPathFor(app, profile, env);
|
|
176
184
|
const files = privateFiles({
|
|
177
|
-
files: [...withSqliteSidecars(store), sends, flood],
|
|
185
|
+
files: [...own.files, ...withSqliteSidecars(store), sends, flood],
|
|
178
186
|
// A folder the owner chose with MESSAGING_STORE may rightly be shared, like the home folder itself.
|
|
179
|
-
dirs: [
|
|
187
|
+
dirs: [
|
|
188
|
+
...own.dirs,
|
|
189
|
+
...(env.MESSAGING_STORE ? [] : [dirname(store)]),
|
|
190
|
+
dirname(sends),
|
|
191
|
+
dirname(flood),
|
|
192
|
+
runsDirFor(app, env),
|
|
193
|
+
],
|
|
180
194
|
});
|
|
181
195
|
return {
|
|
182
196
|
renderer,
|
|
183
197
|
profile,
|
|
184
|
-
fixes: files
|
|
198
|
+
fixes: fixesOf(files),
|
|
185
199
|
report: {
|
|
186
200
|
cli,
|
|
187
201
|
profile,
|
|
@@ -195,15 +209,20 @@ const diagnose = async (command, messenger) => {
|
|
|
195
209
|
flood: floodState(new FloodMemory(flood)),
|
|
196
210
|
runs: { directory: runsDirFor(app, env), kept: listRuns(runsDirFor(app, env)).length },
|
|
197
211
|
// The messenger's own block keeps the keys it gives: some of them are file paths.
|
|
198
|
-
files: files
|
|
199
|
-
? { ...files, problems: files.problems.map(({ path, mode, want }) => ({ path, mode, want })) }
|
|
200
|
-
: files,
|
|
212
|
+
files: filesReport(files),
|
|
201
213
|
[provider]: await (messenger.diagnose?.(command, context) ?? Promise.resolve({})).catch((error) => ({
|
|
202
214
|
error: messageOf(error),
|
|
203
215
|
})),
|
|
204
216
|
},
|
|
205
217
|
};
|
|
206
218
|
};
|
|
219
|
+
/**
|
|
220
|
+
* The config folder is checked even when `<APP>_CONFIG_DIR` names it: it is the tool's own folder, and the
|
|
221
|
+
* credential file falls back to it when there is no keyring.
|
|
222
|
+
*/
|
|
223
|
+
const configFiles = (path) => ({ files: [path], dirs: [dirname(path)] });
|
|
224
|
+
const fixesOf = (files) => (files.checked ? files.problems.map((problem) => problem.fix) : []);
|
|
225
|
+
const filesReport = (files) => files.checked ? { ...files, problems: files.problems.map(({ path, mode, want }) => ({ path, mode, want })) } : files;
|
|
207
226
|
/** What the messenger asked this profile to wait for, still in force — what the next command would be refused. */
|
|
208
227
|
export const floodState = (memory) => {
|
|
209
228
|
const { deadlines, sendBlock } = memory.read();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.428.0",
|
|
4
4
|
"description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|
|
@@ -213,7 +213,7 @@
|
|
|
213
213
|
"@types/node": "^22.20.4",
|
|
214
214
|
"@types/pngjs": "6.0.5",
|
|
215
215
|
"@vitest/coverage-v8": "^5.0.2",
|
|
216
|
-
"@wirecat/cli-base": "0.
|
|
216
|
+
"@wirecat/cli-base": "0.57.0",
|
|
217
217
|
"@wirecat/cli-core-legacy-tags": "npm:@wirecat/cli-core@0.414.0",
|
|
218
218
|
"commander": "15.0.0",
|
|
219
219
|
"cspell": "10.3.6",
|
|
@@ -234,7 +234,7 @@
|
|
|
234
234
|
},
|
|
235
235
|
"peerDependencies": {
|
|
236
236
|
"@napi-rs/canvas": ">=0.1.69 <2",
|
|
237
|
-
"@wirecat/cli-base": ">=0.
|
|
237
|
+
"@wirecat/cli-base": ">=0.57.0 <0.58.0",
|
|
238
238
|
"commander": ">=15"
|
|
239
239
|
},
|
|
240
240
|
"bin": {
|