ccc-notifier 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/LICENSE +21 -0
- package/README.md +171 -0
- package/dist/budget-5H2GQRXH.js +71 -0
- package/dist/chunk-4JNZ7BHO.js +276 -0
- package/dist/chunk-64N5SGTT.js +1024 -0
- package/dist/chunk-DGXUSPS4.js +21 -0
- package/dist/chunk-IIYMGLV4.js +309 -0
- package/dist/chunk-KUJZYZSG.js +66 -0
- package/dist/chunk-L5MLVTA2.js +393 -0
- package/dist/chunk-OEG3AVU6.js +436 -0
- package/dist/chunk-TB5A7U7G.js +82 -0
- package/dist/chunk-TBFKGFZX.js +87 -0
- package/dist/cli.js +717 -0
- package/dist/dashboard-LKK6NOBN.js +14 -0
- package/dist/history-MWFHO5VR.js +114 -0
- package/dist/mute-2JW3NQXN.js +12 -0
- package/dist/setup-RT62VO37.js +15 -0
- package/dist/sweep-XKKODGMA.js +460 -0
- package/dist/track-PBHTVZ3L.js +168 -0
- package/package.json +53 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
notifyOS,
|
|
4
|
+
notifySlack
|
|
5
|
+
} from "./chunk-4JNZ7BHO.js";
|
|
6
|
+
import {
|
|
7
|
+
paths,
|
|
8
|
+
readConfig
|
|
9
|
+
} from "./chunk-IIYMGLV4.js";
|
|
10
|
+
|
|
11
|
+
// src/setup.ts
|
|
12
|
+
import {
|
|
13
|
+
copyFileSync,
|
|
14
|
+
existsSync,
|
|
15
|
+
mkdirSync,
|
|
16
|
+
readFileSync,
|
|
17
|
+
rmSync,
|
|
18
|
+
writeFileSync
|
|
19
|
+
} from "fs";
|
|
20
|
+
import { basename, dirname, join } from "path";
|
|
21
|
+
import { homedir } from "os";
|
|
22
|
+
import { fileURLToPath } from "url";
|
|
23
|
+
import * as p from "@clack/prompts";
|
|
24
|
+
var HOOK_MARKER = "ccc-notifier";
|
|
25
|
+
var HOOK_TIMEOUT = 15;
|
|
26
|
+
var SLACK_PROMPT_CHARS = 100;
|
|
27
|
+
var DEFAULT_BUDGET_USD = 400;
|
|
28
|
+
function isPlainObject(v) {
|
|
29
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
30
|
+
}
|
|
31
|
+
function settingsPath() {
|
|
32
|
+
return process.env.CCCN_CLAUDE_SETTINGS || join(homedir(), ".claude", "settings.json");
|
|
33
|
+
}
|
|
34
|
+
function resolveCliPath() {
|
|
35
|
+
const override = process.env.CCCN_CLI_PATH;
|
|
36
|
+
if (override) return override;
|
|
37
|
+
const here = fileURLToPath(import.meta.url);
|
|
38
|
+
const dir = dirname(here);
|
|
39
|
+
if (basename(dir) === "dist") return join(dir, "cli.js");
|
|
40
|
+
return join(dir, "..", "dist", "cli.js");
|
|
41
|
+
}
|
|
42
|
+
function buildHookCommand() {
|
|
43
|
+
let nodePath = process.execPath;
|
|
44
|
+
let cliPath = resolveCliPath();
|
|
45
|
+
if (process.platform === "win32") {
|
|
46
|
+
nodePath = nodePath.replace(/\\/g, "/");
|
|
47
|
+
cliPath = cliPath.replace(/\\/g, "/");
|
|
48
|
+
}
|
|
49
|
+
return `"${nodePath}" "${cliPath}" track`;
|
|
50
|
+
}
|
|
51
|
+
function matchesMarker(command) {
|
|
52
|
+
return command.includes(HOOK_MARKER);
|
|
53
|
+
}
|
|
54
|
+
function isOurStopEntry(entry) {
|
|
55
|
+
if (!isPlainObject(entry)) return false;
|
|
56
|
+
const hooks = entry.hooks;
|
|
57
|
+
if (!Array.isArray(hooks)) return false;
|
|
58
|
+
return hooks.some(
|
|
59
|
+
(h) => isPlainObject(h) && typeof h.command === "string" && matchesMarker(h.command)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
function ourStopEntry(command) {
|
|
63
|
+
return { hooks: [{ type: "command", command, timeout: HOOK_TIMEOUT }] };
|
|
64
|
+
}
|
|
65
|
+
function backupSettings(path) {
|
|
66
|
+
const backupPath = `${path}.bak-${Date.now()}`;
|
|
67
|
+
copyFileSync(path, backupPath);
|
|
68
|
+
return backupPath;
|
|
69
|
+
}
|
|
70
|
+
function printManualSnippet(path, command, reason) {
|
|
71
|
+
const snippet = JSON.stringify(ourStopEntry(command), null, 2);
|
|
72
|
+
console.error(`ERROR: ${reason}: ${path}`);
|
|
73
|
+
console.error("\u5B89\u5168\u306E\u305F\u3081 settings.json \u306E\u81EA\u52D5\u7DE8\u96C6\u3092\u4E2D\u6B62\u3057\u307E\u3057\u305F\u3002");
|
|
74
|
+
console.error("\u4EE5\u4E0B\u306E JSON \u3092 settings.json \u306E hooks.Stop \u914D\u5217\u306B\u624B\u52D5\u3067\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044:");
|
|
75
|
+
console.error("");
|
|
76
|
+
console.error(snippet);
|
|
77
|
+
console.error("");
|
|
78
|
+
}
|
|
79
|
+
function makeTestRecord() {
|
|
80
|
+
return {
|
|
81
|
+
schemaVersion: 1,
|
|
82
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
83
|
+
sessionId: "cccn-setup-test",
|
|
84
|
+
project: process.cwd(),
|
|
85
|
+
gitBranch: null,
|
|
86
|
+
models: ["claude-fable-5"],
|
|
87
|
+
tokens: { input: 100, output: 50, cacheWrite5m: 0, cacheWrite1h: 0, cacheRead: 0 },
|
|
88
|
+
sidechainTokens: null,
|
|
89
|
+
apiCalls: 1,
|
|
90
|
+
costUSD: 0.01,
|
|
91
|
+
costJPY: 1.5,
|
|
92
|
+
fxRate: 150,
|
|
93
|
+
fxSource: "fixed",
|
|
94
|
+
prompt: "\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u5B8C\u4E86\u30C6\u30B9\u30C8"
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function takeValue(argv, i, prefix) {
|
|
98
|
+
const a = argv[i];
|
|
99
|
+
if (a.startsWith(prefix + "=")) return a.slice(prefix.length + 1);
|
|
100
|
+
return argv[i + 1];
|
|
101
|
+
}
|
|
102
|
+
function parseInitFlags(argv) {
|
|
103
|
+
const flags = { yes: false, osOnly: false, slackOnly: false };
|
|
104
|
+
for (let i = 0; i < argv.length; i++) {
|
|
105
|
+
const a = argv[i];
|
|
106
|
+
if (a === "--yes" || a === "-y") flags.yes = true;
|
|
107
|
+
else if (a === "--os-only") flags.osOnly = true;
|
|
108
|
+
else if (a === "--slack-only") flags.slackOnly = true;
|
|
109
|
+
else if (a === "--slack-webhook" || a.startsWith("--slack-webhook=")) {
|
|
110
|
+
flags.slackWebhook = takeValue(argv, i, "--slack-webhook");
|
|
111
|
+
if (!a.includes("=")) i++;
|
|
112
|
+
} else if (a === "--label" || a.startsWith("--label=")) {
|
|
113
|
+
flags.label = takeValue(argv, i, "--label");
|
|
114
|
+
if (!a.includes("=")) i++;
|
|
115
|
+
} else if (a === "--rate" || a.startsWith("--rate=")) {
|
|
116
|
+
flags.rate = takeValue(argv, i, "--rate");
|
|
117
|
+
if (!a.includes("=")) i++;
|
|
118
|
+
} else if (a === "--budget" || a.startsWith("--budget=")) {
|
|
119
|
+
flags.budget = takeValue(argv, i, "--budget");
|
|
120
|
+
if (!a.includes("=")) i++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return flags;
|
|
124
|
+
}
|
|
125
|
+
function parseUninstallFlags(argv) {
|
|
126
|
+
const flags = { yes: false, purge: false };
|
|
127
|
+
for (const a of argv) {
|
|
128
|
+
if (a === "--yes" || a === "-y") flags.yes = true;
|
|
129
|
+
else if (a === "--purge") flags.purge = true;
|
|
130
|
+
}
|
|
131
|
+
return flags;
|
|
132
|
+
}
|
|
133
|
+
function mergeSettings(sPath, command) {
|
|
134
|
+
if (!existsSync(sPath)) {
|
|
135
|
+
mkdirSync(dirname(sPath), { recursive: true });
|
|
136
|
+
const fresh = { hooks: { Stop: [ourStopEntry(command)] } };
|
|
137
|
+
writeFileSync(sPath, JSON.stringify(fresh, null, 2) + "\n", "utf8");
|
|
138
|
+
return { ok: true, backupPath: null };
|
|
139
|
+
}
|
|
140
|
+
const raw = readFileSync(sPath, "utf8");
|
|
141
|
+
let parsed;
|
|
142
|
+
try {
|
|
143
|
+
parsed = JSON.parse(raw);
|
|
144
|
+
} catch {
|
|
145
|
+
printManualSnippet(sPath, command, "settings.json \u306E JSON \u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
146
|
+
return { ok: false, backupPath: null };
|
|
147
|
+
}
|
|
148
|
+
if (!isPlainObject(parsed)) {
|
|
149
|
+
printManualSnippet(sPath, command, "settings.json \u306E\u30EB\u30FC\u30C8\u304C\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3067\u306F\u3042\u308A\u307E\u305B\u3093");
|
|
150
|
+
return { ok: false, backupPath: null };
|
|
151
|
+
}
|
|
152
|
+
const obj = parsed;
|
|
153
|
+
const backupPath = backupSettings(sPath);
|
|
154
|
+
if (!isPlainObject(obj.hooks)) obj.hooks = {};
|
|
155
|
+
const hooks = obj.hooks;
|
|
156
|
+
if (!Array.isArray(hooks.Stop)) hooks.Stop = [];
|
|
157
|
+
const stop = hooks.Stop;
|
|
158
|
+
const idx = stop.findIndex(isOurStopEntry);
|
|
159
|
+
if (idx >= 0) {
|
|
160
|
+
const entry = stop[idx];
|
|
161
|
+
const entryHooks = entry.hooks;
|
|
162
|
+
for (const h of entryHooks) {
|
|
163
|
+
if (isPlainObject(h) && typeof h.command === "string" && matchesMarker(h.command)) {
|
|
164
|
+
h.command = command;
|
|
165
|
+
h.timeout = HOOK_TIMEOUT;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
stop.push(ourStopEntry(command));
|
|
170
|
+
}
|
|
171
|
+
writeFileSync(sPath, JSON.stringify(obj, null, 2) + "\n", "utf8");
|
|
172
|
+
return { ok: true, backupPath };
|
|
173
|
+
}
|
|
174
|
+
async function runInit(argv) {
|
|
175
|
+
const flags = parseInitFlags(argv);
|
|
176
|
+
const cfg = readConfig();
|
|
177
|
+
cfg.notify.os = true;
|
|
178
|
+
const budgetDefault = cfg.monthlyBudgetUSD > 0 ? cfg.monthlyBudgetUSD : DEFAULT_BUDGET_USD;
|
|
179
|
+
if (flags.yes) {
|
|
180
|
+
if (flags.slackOnly) {
|
|
181
|
+
if (flags.osOnly) {
|
|
182
|
+
console.error("--slack-only \u3068 --os-only \u306F\u540C\u6642\u306B\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093");
|
|
183
|
+
return 1;
|
|
184
|
+
}
|
|
185
|
+
if (flags.slackWebhook === void 0) {
|
|
186
|
+
console.error("--slack-only \u306F --slack-webhook \u3067 Webhook URL \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
|
|
187
|
+
return 1;
|
|
188
|
+
}
|
|
189
|
+
cfg.notify.os = false;
|
|
190
|
+
cfg.notify.slack = {
|
|
191
|
+
webhookUrl: flags.slackWebhook,
|
|
192
|
+
promptChars: SLACK_PROMPT_CHARS,
|
|
193
|
+
sendFullPrompt: false
|
|
194
|
+
};
|
|
195
|
+
} else if (flags.osOnly) {
|
|
196
|
+
cfg.notify.slack = null;
|
|
197
|
+
} else if (flags.slackWebhook !== void 0) {
|
|
198
|
+
cfg.notify.slack = {
|
|
199
|
+
webhookUrl: flags.slackWebhook,
|
|
200
|
+
promptChars: SLACK_PROMPT_CHARS,
|
|
201
|
+
sendFullPrompt: false
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (flags.label !== void 0) {
|
|
205
|
+
if (flags.label !== "api_equivalent" && flags.label !== "actual") {
|
|
206
|
+
console.error(`--label \u306F api_equivalent \u304B actual \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044(\u53D7\u9818: ${flags.label})`);
|
|
207
|
+
return 1;
|
|
208
|
+
}
|
|
209
|
+
cfg.costLabel = flags.label;
|
|
210
|
+
}
|
|
211
|
+
if (flags.rate !== void 0) {
|
|
212
|
+
const r = Number(flags.rate);
|
|
213
|
+
if (!Number.isFinite(r) || r <= 0) {
|
|
214
|
+
console.error(`--rate \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044(\u53D7\u9818: ${flags.rate})`);
|
|
215
|
+
return 1;
|
|
216
|
+
}
|
|
217
|
+
cfg.fx.fallbackRate = r;
|
|
218
|
+
}
|
|
219
|
+
if (flags.budget !== void 0) {
|
|
220
|
+
const b = Number(flags.budget);
|
|
221
|
+
if (!Number.isFinite(b) || b < 0) {
|
|
222
|
+
console.error(`--budget \u306F 0 \u4EE5\u4E0A\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044(\u53D7\u9818: ${flags.budget})`);
|
|
223
|
+
return 1;
|
|
224
|
+
}
|
|
225
|
+
cfg.monthlyBudgetUSD = b;
|
|
226
|
+
} else {
|
|
227
|
+
cfg.monthlyBudgetUSD = budgetDefault;
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
p.intro("ccc-notifier \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7");
|
|
231
|
+
const channel = await p.select({
|
|
232
|
+
message: "\u901A\u77E5\u30C1\u30E3\u30CD\u30EB\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
233
|
+
options: [
|
|
234
|
+
{ value: "os", label: "OS \u901A\u77E5\u306E\u307F" },
|
|
235
|
+
{ value: "slack", label: "Slack \u306E\u307F(OS \u901A\u77E5\u306A\u3057)" },
|
|
236
|
+
{ value: "both", label: "OS \u901A\u77E5 + Slack" }
|
|
237
|
+
],
|
|
238
|
+
initialValue: "os"
|
|
239
|
+
});
|
|
240
|
+
if (p.isCancel(channel)) {
|
|
241
|
+
p.cancel("\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F");
|
|
242
|
+
return 1;
|
|
243
|
+
}
|
|
244
|
+
if (channel === "both" || channel === "slack") {
|
|
245
|
+
const webhook = await p.text({
|
|
246
|
+
message: "Slack Incoming Webhook URL",
|
|
247
|
+
placeholder: "https://hooks.slack.com/services/...",
|
|
248
|
+
validate: (v) => !v || !v.startsWith("https://") ? "https:// \u3067\u59CB\u307E\u308B URL \u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044" : void 0
|
|
249
|
+
});
|
|
250
|
+
if (p.isCancel(webhook)) {
|
|
251
|
+
p.cancel("\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F");
|
|
252
|
+
return 1;
|
|
253
|
+
}
|
|
254
|
+
cfg.notify.slack = {
|
|
255
|
+
webhookUrl: webhook,
|
|
256
|
+
promptChars: SLACK_PROMPT_CHARS,
|
|
257
|
+
sendFullPrompt: false
|
|
258
|
+
};
|
|
259
|
+
cfg.notify.os = channel === "both";
|
|
260
|
+
} else {
|
|
261
|
+
cfg.notify.slack = null;
|
|
262
|
+
}
|
|
263
|
+
const labelChoice = await p.select({
|
|
264
|
+
message: "\u30B3\u30B9\u30C8\u8868\u793A\u30E9\u30D9\u30EB",
|
|
265
|
+
options: [
|
|
266
|
+
{ value: "api_equivalent", label: "API \u63DB\u7B97(\u5B9A\u984D\u30D7\u30E9\u30F3\u3067\u3082\u5F93\u91CF\u63DB\u7B97\u3067\u8868\u793A)" },
|
|
267
|
+
{ value: "actual", label: "\u5B9F\u984D" }
|
|
268
|
+
],
|
|
269
|
+
initialValue: cfg.costLabel
|
|
270
|
+
});
|
|
271
|
+
if (p.isCancel(labelChoice)) {
|
|
272
|
+
p.cancel("\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F");
|
|
273
|
+
return 1;
|
|
274
|
+
}
|
|
275
|
+
cfg.costLabel = labelChoice === "actual" ? "actual" : "api_equivalent";
|
|
276
|
+
const rateStr = await p.text({
|
|
277
|
+
message: "USD/JPY \u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u70BA\u66FF\u30EC\u30FC\u30C8",
|
|
278
|
+
placeholder: String(cfg.fx.fallbackRate),
|
|
279
|
+
defaultValue: String(cfg.fx.fallbackRate),
|
|
280
|
+
validate: (v) => {
|
|
281
|
+
if (v === void 0 || v === "") return void 0;
|
|
282
|
+
const n = Number(v);
|
|
283
|
+
return !Number.isFinite(n) || n <= 0 ? "\u6B63\u306E\u6570\u5024\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044" : void 0;
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
if (p.isCancel(rateStr)) {
|
|
287
|
+
p.cancel("\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F");
|
|
288
|
+
return 1;
|
|
289
|
+
}
|
|
290
|
+
const parsedRate = Number(rateStr);
|
|
291
|
+
if (Number.isFinite(parsedRate) && parsedRate > 0) cfg.fx.fallbackRate = parsedRate;
|
|
292
|
+
const budgetStr = await p.text({
|
|
293
|
+
message: "\u6708\u306E\u4E88\u7B97(USD\u3001\u65E2\u5B9A $400\u30020 \u3067\u7121\u52B9\u30FB\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u306B\u5F53\u6708\u306E\u4F7F\u7528\u7387\u3092\u8868\u793A)",
|
|
294
|
+
placeholder: String(budgetDefault),
|
|
295
|
+
defaultValue: String(budgetDefault),
|
|
296
|
+
validate: (v) => {
|
|
297
|
+
if (v === void 0 || v === "") return void 0;
|
|
298
|
+
const n = Number(v);
|
|
299
|
+
return !Number.isFinite(n) || n < 0 ? "0 \u4EE5\u4E0A\u306E\u6570\u5024\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044" : void 0;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
if (p.isCancel(budgetStr)) {
|
|
303
|
+
p.cancel("\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F");
|
|
304
|
+
return 1;
|
|
305
|
+
}
|
|
306
|
+
const parsedBudget = Number(budgetStr);
|
|
307
|
+
if (Number.isFinite(parsedBudget) && parsedBudget >= 0) cfg.monthlyBudgetUSD = parsedBudget;
|
|
308
|
+
p.outro("\u8A2D\u5B9A\u3092\u9069\u7528\u3057\u307E\u3059");
|
|
309
|
+
}
|
|
310
|
+
const cccn = paths();
|
|
311
|
+
writeFileSync(cccn.configFile, JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
312
|
+
const command = buildHookCommand();
|
|
313
|
+
const sPath = settingsPath();
|
|
314
|
+
const result = mergeSettings(sPath, command);
|
|
315
|
+
if (!result.ok) return 1;
|
|
316
|
+
const testRecord = makeTestRecord();
|
|
317
|
+
await notifyOS(testRecord, cfg);
|
|
318
|
+
if (cfg.notify.slack) {
|
|
319
|
+
await notifySlack(testRecord, cfg);
|
|
320
|
+
}
|
|
321
|
+
console.log("");
|
|
322
|
+
console.log(`settings \u3092\u66F4\u65B0\u3057\u307E\u3057\u305F: ${sPath}`);
|
|
323
|
+
console.log(
|
|
324
|
+
result.backupPath ? ` \u30D0\u30C3\u30AF\u30A2\u30C3\u30D7: ${result.backupPath}` : " (settings.json \u3092\u65B0\u898F\u4F5C\u6210\u3057\u305F\u305F\u3081\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u306F\u3042\u308A\u307E\u305B\u3093)"
|
|
325
|
+
);
|
|
326
|
+
console.log(` \u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB: ${cccn.configFile}`);
|
|
327
|
+
console.log("Claude Code \u3067\u4F55\u304B\u5B9F\u884C\u3059\u308B\u3068\u901A\u77E5\u304C\u5C4A\u304D\u307E\u3059\u3002\u78BA\u8A8D: npx ccc-notifier doctor");
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
330
|
+
async function runUninstall(argv) {
|
|
331
|
+
const flags = parseUninstallFlags(argv);
|
|
332
|
+
const sPath = settingsPath();
|
|
333
|
+
if (!existsSync(sPath)) {
|
|
334
|
+
console.log("\u767B\u9332\u306A\u3057: settings.json \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002");
|
|
335
|
+
} else {
|
|
336
|
+
const raw = readFileSync(sPath, "utf8");
|
|
337
|
+
let parsed;
|
|
338
|
+
let parseOk = true;
|
|
339
|
+
try {
|
|
340
|
+
parsed = JSON.parse(raw);
|
|
341
|
+
} catch {
|
|
342
|
+
parseOk = false;
|
|
343
|
+
}
|
|
344
|
+
if (!parseOk || !isPlainObject(parsed)) {
|
|
345
|
+
console.error(`settings.json \u3092\u89E3\u6790\u3067\u304D\u306A\u3044\u305F\u3081\u7DE8\u96C6\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3057\u305F: ${sPath}`);
|
|
346
|
+
} else {
|
|
347
|
+
const obj = parsed;
|
|
348
|
+
const hooks = isPlainObject(obj.hooks) ? obj.hooks : null;
|
|
349
|
+
const stop = hooks && Array.isArray(hooks.Stop) ? hooks.Stop : null;
|
|
350
|
+
const hasMarker = stop ? stop.some(isOurStopEntry) : false;
|
|
351
|
+
if (!hasMarker || !hooks || !stop) {
|
|
352
|
+
console.log("\u767B\u9332\u306A\u3057: \u672C\u30C4\u30FC\u30EB\u306E Stop \u30D5\u30C3\u30AF\u306F\u767B\u9332\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002");
|
|
353
|
+
} else {
|
|
354
|
+
const backupPath = backupSettings(sPath);
|
|
355
|
+
const filtered = stop.filter((e) => !isOurStopEntry(e));
|
|
356
|
+
if (filtered.length === 0) {
|
|
357
|
+
delete hooks.Stop;
|
|
358
|
+
} else {
|
|
359
|
+
hooks.Stop = filtered;
|
|
360
|
+
}
|
|
361
|
+
writeFileSync(sPath, JSON.stringify(obj, null, 2) + "\n", "utf8");
|
|
362
|
+
console.log(`\u672C\u30C4\u30FC\u30EB\u306E Stop \u30D5\u30C3\u30AF\u3092\u9664\u53BB\u3057\u307E\u3057\u305F: ${sPath}`);
|
|
363
|
+
console.log(` \u30D0\u30C3\u30AF\u30A2\u30C3\u30D7: ${backupPath}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (flags.purge) {
|
|
368
|
+
const home = paths().home;
|
|
369
|
+
let doPurge = flags.yes;
|
|
370
|
+
if (!flags.yes) {
|
|
371
|
+
const confirmed = await p.confirm({
|
|
372
|
+
message: `\u30C7\u30FC\u30BF\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u5B8C\u5168\u306B\u524A\u9664\u3057\u307E\u3059\u304B?(${home})`,
|
|
373
|
+
initialValue: false
|
|
374
|
+
});
|
|
375
|
+
if (p.isCancel(confirmed)) {
|
|
376
|
+
p.cancel("\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F");
|
|
377
|
+
return 0;
|
|
378
|
+
}
|
|
379
|
+
doPurge = confirmed;
|
|
380
|
+
}
|
|
381
|
+
if (doPurge) {
|
|
382
|
+
rmSync(home, { recursive: true, force: true });
|
|
383
|
+
console.log(`\u30C7\u30FC\u30BF\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u524A\u9664\u3057\u307E\u3057\u305F: ${home}`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export {
|
|
390
|
+
matchesMarker,
|
|
391
|
+
runInit,
|
|
392
|
+
runUninstall
|
|
393
|
+
};
|