calendit 1.0.2 → 2026.4.26
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/README.md +81 -62
- package/dist/commands/accounts.d.ts +4 -0
- package/dist/commands/accounts.js +26 -0
- package/dist/commands/add.js +8 -0
- package/dist/commands/apply.js +5 -1
- package/dist/commands/auth.js +11 -2
- package/dist/commands/config.js +50 -16
- package/dist/commands/macos.d.ts +3 -0
- package/dist/commands/macos.js +401 -0
- package/dist/commands/onboard.d.ts +2 -0
- package/dist/commands/onboard.js +79 -0
- package/dist/commands/query.js +2 -2
- package/dist/commands/shared.d.ts +3 -2
- package/dist/commands/shared.js +21 -5
- package/dist/core/accountStatus.d.ts +18 -0
- package/dist/core/accountStatus.js +74 -0
- package/dist/core/auth.js +7 -11
- package/dist/core/authStatus.d.ts +20 -0
- package/dist/core/authStatus.js +82 -0
- package/dist/core/config.d.ts +11 -1
- package/dist/core/config.js +73 -6
- package/dist/core/datetime.js +3 -2
- package/dist/core/errors.d.ts +3 -0
- package/dist/core/errors.js +5 -0
- package/dist/core/eventkitBridgeFetch.d.ts +26 -0
- package/dist/core/eventkitBridgeFetch.js +159 -0
- package/dist/core/eventkitEnvFromConfig.d.ts +7 -0
- package/dist/core/eventkitEnvFromConfig.js +24 -0
- package/dist/core/eventkitHelper.d.ts +50 -0
- package/dist/core/eventkitHelper.js +336 -0
- package/dist/core/formatter.d.ts +41 -0
- package/dist/core/formatter.js +79 -0
- package/dist/core/i18n.d.ts +7 -0
- package/dist/core/i18n.js +52 -0
- package/dist/core/localeBootstrap.d.ts +12 -0
- package/dist/core/localeBootstrap.js +74 -0
- package/dist/core/logger.d.ts +2 -0
- package/dist/core/logger.js +5 -0
- package/dist/core/macosBridgeApp.d.ts +12 -0
- package/dist/core/macosBridgeApp.js +83 -0
- package/dist/core/macosTerminalRelay.d.ts +12 -0
- package/dist/core/macosTerminalRelay.js +62 -0
- package/dist/generated/locale-keys.d.ts +3 -0
- package/dist/generated/locale-keys.js +90 -0
- package/dist/index.js +103 -18
- package/dist/locales/en.json +128 -0
- package/dist/locales/ja.json +128 -0
- package/dist/services/macos.d.ts +14 -0
- package/dist/services/macos.js +115 -0
- package/dist/test_runner.js +11 -2
- package/dist/types/index.d.ts +12 -1
- package/package.json +16 -5
package/dist/core/logger.js
CHANGED
|
@@ -25,6 +25,11 @@ function writeToDebugDump(line) {
|
|
|
25
25
|
return;
|
|
26
26
|
debugStream.write(`${line}\n`);
|
|
27
27
|
}
|
|
28
|
+
/** ログプレフィックスなしの標準出力(表形式のユーザー向け表示用) */
|
|
29
|
+
export function writeStdoutLine(line) {
|
|
30
|
+
writeToDebugDump(line);
|
|
31
|
+
process.stdout.write(`${line}\n`);
|
|
32
|
+
}
|
|
28
33
|
function stringifyArgs(args) {
|
|
29
34
|
return args
|
|
30
35
|
.map((arg) => {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Directory of the installed `calendit` npm package (where package.json with name "calendit" lives), or null.
|
|
3
|
+
* Used to locate `native/eventkit-bridge` in a full clone; absent from the published npm tarball.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveCalenditPackageRootFromModule(): string | null;
|
|
6
|
+
/** `native/eventkit-bridge` with `Package.swift` (source tree), or null. */
|
|
7
|
+
export declare function resolveEventkitBridgeRepoPath(): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the CalenditEventKitBridge .app path for `calendit macos bridge start`.
|
|
10
|
+
* Order: CALENDIT_EVENTKIT_BRIDGE_APP, ~/Applications, /Applications, repo .build (dev).
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveEventkitBridgeAppBundlePath(): string | null;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { defaultCalenditDataDir } from "./eventkitHelper.js";
|
|
6
|
+
function isAppBundle(p) {
|
|
7
|
+
return p.endsWith(".app") && fs.existsSync(path.join(p, "Contents/Info.plist"));
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Directory of the installed `calendit` npm package (where package.json with name "calendit" lives), or null.
|
|
11
|
+
* Used to locate `native/eventkit-bridge` in a full clone; absent from the published npm tarball.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveCalenditPackageRootFromModule() {
|
|
14
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
for (let i = 0; i < 20; i++) {
|
|
16
|
+
const pkg = path.join(dir, "package.json");
|
|
17
|
+
if (fs.existsSync(pkg)) {
|
|
18
|
+
try {
|
|
19
|
+
const raw = fs.readFileSync(pkg, "utf8");
|
|
20
|
+
const j = JSON.parse(raw);
|
|
21
|
+
if (j.name === "calendit") {
|
|
22
|
+
return dir;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// ignore
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const parent = path.dirname(dir);
|
|
30
|
+
if (parent === dir) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
dir = parent;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
/** `native/eventkit-bridge` with `Package.swift` (source tree), or null. */
|
|
38
|
+
export function resolveEventkitBridgeRepoPath() {
|
|
39
|
+
const override = process.env.CALENDIT_EVENTKIT_BRIDGE_ROOT?.trim();
|
|
40
|
+
if (override) {
|
|
41
|
+
const p = path.resolve(override);
|
|
42
|
+
if (fs.existsSync(path.join(p, "Package.swift"))) {
|
|
43
|
+
return p;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const root = resolveCalenditPackageRootFromModule();
|
|
48
|
+
if (!root) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const br = path.join(root, "native/eventkit-bridge");
|
|
52
|
+
if (fs.existsSync(path.join(br, "Package.swift"))) {
|
|
53
|
+
return br;
|
|
54
|
+
}
|
|
55
|
+
const fetched = path.join(defaultCalenditDataDir(), "fetched-eventkit-bridge");
|
|
56
|
+
if (fs.existsSync(path.join(fetched, "Package.swift"))) {
|
|
57
|
+
return fetched;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the CalenditEventKitBridge .app path for `calendit macos bridge start`.
|
|
63
|
+
* Order: CALENDIT_EVENTKIT_BRIDGE_APP, ~/Applications, /Applications, repo .build (dev).
|
|
64
|
+
*/
|
|
65
|
+
export function resolveEventkitBridgeAppBundlePath() {
|
|
66
|
+
const fromEnv = process.env.CALENDIT_EVENTKIT_BRIDGE_APP?.trim();
|
|
67
|
+
if (fromEnv && isAppBundle(fromEnv)) {
|
|
68
|
+
return fromEnv;
|
|
69
|
+
}
|
|
70
|
+
const homeApps = path.join(os.homedir(), "Applications/CalenditEventKitBridge.app");
|
|
71
|
+
if (isAppBundle(homeApps)) {
|
|
72
|
+
return homeApps;
|
|
73
|
+
}
|
|
74
|
+
const global = "/Applications/CalenditEventKitBridge.app";
|
|
75
|
+
if (isAppBundle(global)) {
|
|
76
|
+
return global;
|
|
77
|
+
}
|
|
78
|
+
const dev = path.join(process.cwd(), "native/eventkit-bridge/.build/CalenditEventKitBridge.app");
|
|
79
|
+
if (isAppBundle(dev)) {
|
|
80
|
+
return dev;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a shell one-liner: optional exports, cd cwd, then `calendit macos …` or `node …/index.js macos …`.
|
|
3
|
+
*/
|
|
4
|
+
export declare function buildMacosExternalShellLine(cwd: string, macosArgv: string[]): string;
|
|
5
|
+
/** Tell Terminal.app to run `shellLine` (Calendar TCC applies to Terminal, not IDE host). */
|
|
6
|
+
export declare function buildMacosTerminalSessionLine(cwd: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Run `shellLine` in Terminal.app. If a window already exists, reuse **window 1**
|
|
9
|
+
* (`do script … in window 1`) so each `external` call does not spawn a separate window.
|
|
10
|
+
* (Terminal may still open a **new tab** per call, depending on user preferences.)
|
|
11
|
+
*/
|
|
12
|
+
export declare function openTerminalAndRunShellLine(shellLine: string): void;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { execFileSync } from "child_process";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import * as process from "process";
|
|
4
|
+
/** POSIX single-quoted string for sh -c / do script. */
|
|
5
|
+
function shSingleQuote(s) {
|
|
6
|
+
return `'${String(s).replace(/'/g, `'"'"'`)}'`;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Build a shell one-liner: optional exports, cd cwd, then `calendit macos …` or `node …/index.js macos …`.
|
|
10
|
+
*/
|
|
11
|
+
export function buildMacosExternalShellLine(cwd, macosArgv) {
|
|
12
|
+
const argv1 = process.argv[1];
|
|
13
|
+
let inv;
|
|
14
|
+
if (argv1?.endsWith(".js")) {
|
|
15
|
+
inv = `node ${shSingleQuote(path.resolve(argv1))}`;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
inv = "calendit";
|
|
19
|
+
}
|
|
20
|
+
const exports = [];
|
|
21
|
+
if (process.env.CALENDIT_CONFIG_DIR) {
|
|
22
|
+
exports.push(`export CALENDIT_CONFIG_DIR=${shSingleQuote(process.env.CALENDIT_CONFIG_DIR)}`);
|
|
23
|
+
}
|
|
24
|
+
if (process.env.CALENDIT_EVENTKIT_HELPER) {
|
|
25
|
+
exports.push(`export CALENDIT_EVENTKIT_HELPER=${shSingleQuote(process.env.CALENDIT_EVENTKIT_HELPER)}`);
|
|
26
|
+
}
|
|
27
|
+
const macosRest = macosArgv.join(" ");
|
|
28
|
+
const exportPrefix = exports.length ? `${exports.join(" && ")} && ` : "";
|
|
29
|
+
return `${exportPrefix}cd ${shSingleQuote(cwd)} && ${inv} macos ${macosRest}`;
|
|
30
|
+
}
|
|
31
|
+
/** Tell Terminal.app to run `shellLine` (Calendar TCC applies to Terminal, not IDE host). */
|
|
32
|
+
export function buildMacosTerminalSessionLine(cwd) {
|
|
33
|
+
const exports = [];
|
|
34
|
+
if (process.env.CALENDIT_CONFIG_DIR) {
|
|
35
|
+
exports.push(`export CALENDIT_CONFIG_DIR=${shSingleQuote(process.env.CALENDIT_CONFIG_DIR)}`);
|
|
36
|
+
}
|
|
37
|
+
if (process.env.CALENDIT_EVENTKIT_HELPER) {
|
|
38
|
+
exports.push(`export CALENDIT_EVENTKIT_HELPER=${shSingleQuote(process.env.CALENDIT_EVENTKIT_HELPER)}`);
|
|
39
|
+
}
|
|
40
|
+
const shell = process.env.SHELL || "/bin/zsh";
|
|
41
|
+
const exportPrefix = exports.length ? `${exports.join(" && ")} && ` : "";
|
|
42
|
+
return `${exportPrefix}cd ${shSingleQuote(cwd)} && exec ${shSingleQuote(shell)} -l`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Run `shellLine` in Terminal.app. If a window already exists, reuse **window 1**
|
|
46
|
+
* (`do script … in window 1`) so each `external` call does not spawn a separate window.
|
|
47
|
+
* (Terminal may still open a **new tab** per call, depending on user preferences.)
|
|
48
|
+
*/
|
|
49
|
+
export function openTerminalAndRunShellLine(shellLine) {
|
|
50
|
+
const line = JSON.stringify(shellLine);
|
|
51
|
+
const script = [
|
|
52
|
+
`tell application "Terminal"`,
|
|
53
|
+
` activate`,
|
|
54
|
+
` if (count of windows) is 0 then`,
|
|
55
|
+
` do script ${line}`,
|
|
56
|
+
` else`,
|
|
57
|
+
` do script ${line} in window 1`,
|
|
58
|
+
` end if`,
|
|
59
|
+
`end tell`,
|
|
60
|
+
].join("\n");
|
|
61
|
+
execFileSync("osascript", ["-e", script], { stdio: "inherit" });
|
|
62
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Generated by scripts/gen-locale-keys.mjs — do not edit by hand */
|
|
2
|
+
export type LocaleKey = "common.hint" | "config.check.contexts" | "config.check.contextsNone" | "config.check.fileLine" | "config.check.googleNotSet" | "config.check.googleOk" | "config.check.header" | "config.check.outlookNotSet" | "config.check.outlookOk" | "config.check.uiLocale" | "config.cmd.contextDeleted" | "config.cmd.contextSaved" | "config.cmd.googleSaved" | "config.cmd.localeSet" | "config.cmd.macosTransportSet" | "config.cmd.outlookSaved" | "errors.api.prefix" | "errors.apiWithStatus" | "errors.auth.googleLoginHint" | "errors.auth.prefix" | "errors.config.fileNotFound" | "errors.config.fileNotFoundHint" | "errors.config.invalidFormat" | "errors.config.invalidFormatDetails" | "errors.config.prefix" | "errors.config.readFailed" | "errors.config.readFailedHint" | "errors.context.loadFailed" | "errors.context.loadFailedHint" | "errors.context.missing" | "errors.context.missingHint" | "errors.datetime.invalidFormat" | "errors.datetime.invalidParse" | "errors.datetime.invalidParseHint" | "errors.service.googleCredsNotSet" | "errors.service.googleCredsNotSetHint" | "errors.service.outlookCredsNotSet" | "errors.service.outlookCredsNotSetHint" | "errors.unknown" | "errors.validation.prefix" | "eventkit.bridge.bridgeError" | "eventkit.bridge.closedWithoutResponse" | "eventkit.bridge.connectFailed" | "eventkit.bridge.hintStartBridge" | "eventkit.bridge.invalidJson" | "eventkit.bridge.timeout" | "eventkit.bridge.tokenMissing" | "eventkit.helper.exitCode" | "eventkit.helper.hintBuild" | "eventkit.helper.missing" | "eventkit.helper.timeout" | "locale.bootstrap.choiceEn" | "locale.bootstrap.choiceJa" | "locale.bootstrap.prompt" | "macos.bridge.buildFailed" | "macos.bridge.buildNoSource" | "macos.bridge.buildOk" | "macos.bridge.fetchBuildPrompt" | "macos.bridge.fetchCancel" | "macos.bridge.fetchConfirm" | "macos.bridge.fetchFailed" | "macos.bridge.fetchIntro" | "macos.bridge.fetchNoTty" | "macos.bridge.fetchOk" | "macos.bridge.fetchPlan" | "macos.bridge.fetchSizeKnown" | "macos.bridge.fetchSizeUnknown" | "macos.bridge.fetchSkipsExisting" | "macos.bridge.notFound" | "macos.bridge.openFailed" | "macos.bridge.opened" | "macos.external.badSub" | "macos.external.badSubHint" | "macos.external.jsonOnlyForList" | "macos.external.onlyDarwin" | "macos.external.opened" | "macos.external.osascriptFailed" | "macos.external.suggestionWhenDenied" | "macos.setup.canceled" | "macos.setup.contextName" | "macos.setup.contextNameRequired" | "macos.setup.noCalendars" | "macos.setup.noTransport" | "macos.setup.notDarwin" | "macos.setup.pickCalendar" | "macos.setup.saved" | "macos.setup.startBridgePrompt" | "macos.setup.tcc";
|
|
3
|
+
export declare const LOCALE_KEYS: readonly LocaleKey[];
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export const LOCALE_KEYS = [
|
|
2
|
+
"common.hint",
|
|
3
|
+
"config.check.contexts",
|
|
4
|
+
"config.check.contextsNone",
|
|
5
|
+
"config.check.fileLine",
|
|
6
|
+
"config.check.googleNotSet",
|
|
7
|
+
"config.check.googleOk",
|
|
8
|
+
"config.check.header",
|
|
9
|
+
"config.check.outlookNotSet",
|
|
10
|
+
"config.check.outlookOk",
|
|
11
|
+
"config.check.uiLocale",
|
|
12
|
+
"config.cmd.contextDeleted",
|
|
13
|
+
"config.cmd.contextSaved",
|
|
14
|
+
"config.cmd.googleSaved",
|
|
15
|
+
"config.cmd.localeSet",
|
|
16
|
+
"config.cmd.macosTransportSet",
|
|
17
|
+
"config.cmd.outlookSaved",
|
|
18
|
+
"errors.api.prefix",
|
|
19
|
+
"errors.apiWithStatus",
|
|
20
|
+
"errors.auth.googleLoginHint",
|
|
21
|
+
"errors.auth.prefix",
|
|
22
|
+
"errors.config.fileNotFound",
|
|
23
|
+
"errors.config.fileNotFoundHint",
|
|
24
|
+
"errors.config.invalidFormat",
|
|
25
|
+
"errors.config.invalidFormatDetails",
|
|
26
|
+
"errors.config.prefix",
|
|
27
|
+
"errors.config.readFailed",
|
|
28
|
+
"errors.config.readFailedHint",
|
|
29
|
+
"errors.context.loadFailed",
|
|
30
|
+
"errors.context.loadFailedHint",
|
|
31
|
+
"errors.context.missing",
|
|
32
|
+
"errors.context.missingHint",
|
|
33
|
+
"errors.datetime.invalidFormat",
|
|
34
|
+
"errors.datetime.invalidParse",
|
|
35
|
+
"errors.datetime.invalidParseHint",
|
|
36
|
+
"errors.service.googleCredsNotSet",
|
|
37
|
+
"errors.service.googleCredsNotSetHint",
|
|
38
|
+
"errors.service.outlookCredsNotSet",
|
|
39
|
+
"errors.service.outlookCredsNotSetHint",
|
|
40
|
+
"errors.unknown",
|
|
41
|
+
"errors.validation.prefix",
|
|
42
|
+
"eventkit.bridge.bridgeError",
|
|
43
|
+
"eventkit.bridge.closedWithoutResponse",
|
|
44
|
+
"eventkit.bridge.connectFailed",
|
|
45
|
+
"eventkit.bridge.hintStartBridge",
|
|
46
|
+
"eventkit.bridge.invalidJson",
|
|
47
|
+
"eventkit.bridge.timeout",
|
|
48
|
+
"eventkit.bridge.tokenMissing",
|
|
49
|
+
"eventkit.helper.exitCode",
|
|
50
|
+
"eventkit.helper.hintBuild",
|
|
51
|
+
"eventkit.helper.missing",
|
|
52
|
+
"eventkit.helper.timeout",
|
|
53
|
+
"locale.bootstrap.choiceEn",
|
|
54
|
+
"locale.bootstrap.choiceJa",
|
|
55
|
+
"locale.bootstrap.prompt",
|
|
56
|
+
"macos.bridge.buildFailed",
|
|
57
|
+
"macos.bridge.buildNoSource",
|
|
58
|
+
"macos.bridge.buildOk",
|
|
59
|
+
"macos.bridge.fetchBuildPrompt",
|
|
60
|
+
"macos.bridge.fetchCancel",
|
|
61
|
+
"macos.bridge.fetchConfirm",
|
|
62
|
+
"macos.bridge.fetchFailed",
|
|
63
|
+
"macos.bridge.fetchIntro",
|
|
64
|
+
"macos.bridge.fetchNoTty",
|
|
65
|
+
"macos.bridge.fetchOk",
|
|
66
|
+
"macos.bridge.fetchPlan",
|
|
67
|
+
"macos.bridge.fetchSizeKnown",
|
|
68
|
+
"macos.bridge.fetchSizeUnknown",
|
|
69
|
+
"macos.bridge.fetchSkipsExisting",
|
|
70
|
+
"macos.bridge.notFound",
|
|
71
|
+
"macos.bridge.openFailed",
|
|
72
|
+
"macos.bridge.opened",
|
|
73
|
+
"macos.external.badSub",
|
|
74
|
+
"macos.external.badSubHint",
|
|
75
|
+
"macos.external.jsonOnlyForList",
|
|
76
|
+
"macos.external.onlyDarwin",
|
|
77
|
+
"macos.external.opened",
|
|
78
|
+
"macos.external.osascriptFailed",
|
|
79
|
+
"macos.external.suggestionWhenDenied",
|
|
80
|
+
"macos.setup.canceled",
|
|
81
|
+
"macos.setup.contextName",
|
|
82
|
+
"macos.setup.contextNameRequired",
|
|
83
|
+
"macos.setup.noCalendars",
|
|
84
|
+
"macos.setup.noTransport",
|
|
85
|
+
"macos.setup.notDarwin",
|
|
86
|
+
"macos.setup.pickCalendar",
|
|
87
|
+
"macos.setup.saved",
|
|
88
|
+
"macos.setup.startBridgePrompt",
|
|
89
|
+
"macos.setup.tcc",
|
|
90
|
+
];
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
import { createRequire } from "module";
|
|
2
3
|
import { AuthManager } from "./core/auth.js";
|
|
3
4
|
import { ConfigManager } from "./core/config.js";
|
|
5
|
+
import { applyEventkitConfigToEnvAfterLoad } from "./core/eventkitEnvFromConfig.js";
|
|
4
6
|
import { ApiError, AuthError, CalendarError, ConfigError, ValidationError } from "./core/errors.js";
|
|
5
7
|
import { logger, setDebugDump, setLogLevel } from "./core/logger.js";
|
|
8
|
+
import { t } from "./core/i18n.js";
|
|
9
|
+
import { applyResolvedLocale, ensureLocalePreference, shouldSkipLocaleBootstrap } from "./core/localeBootstrap.js";
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const { version } = require("../package.json");
|
|
6
12
|
import { registerAuthCommands } from "./commands/auth.js";
|
|
7
13
|
import { registerConfigCommands } from "./commands/config.js";
|
|
8
14
|
import { registerQueryCommand } from "./commands/query.js";
|
|
9
15
|
import { registerApplyCommand } from "./commands/apply.js";
|
|
10
16
|
import { registerAddCommand } from "./commands/add.js";
|
|
11
17
|
import { registerCalCommands } from "./commands/cal.js";
|
|
18
|
+
import { registerAccountsCommands } from "./commands/accounts.js";
|
|
19
|
+
import { registerMacosCommands } from "./commands/macos.js";
|
|
20
|
+
import { registerOnboardCommand } from "./commands/onboard.js";
|
|
12
21
|
const program = new Command();
|
|
13
22
|
const deps = {
|
|
14
23
|
config: new ConfigManager(),
|
|
@@ -17,10 +26,17 @@ const deps = {
|
|
|
17
26
|
program
|
|
18
27
|
.name("calendit")
|
|
19
28
|
.description("Terminal-based Calendar Management Tool")
|
|
20
|
-
.version(
|
|
29
|
+
.version(version)
|
|
21
30
|
.option("--verbose", "Enable verbose debug logs", false)
|
|
22
|
-
.option("--debug-dump <file>", "Write all logs to a file")
|
|
23
|
-
|
|
31
|
+
.option("--debug-dump <file>", "Write all logs to a file")
|
|
32
|
+
.option("--locale <code>", "UI language: en or ja (overrides config for this run)");
|
|
33
|
+
program.hook("preAction", async (thisCommand) => {
|
|
34
|
+
await ensureLocalePreference(program, deps.config);
|
|
35
|
+
if (!shouldSkipLocaleBootstrap()) {
|
|
36
|
+
await deps.config.loadOptional();
|
|
37
|
+
applyEventkitConfigToEnvAfterLoad(deps.config);
|
|
38
|
+
}
|
|
39
|
+
applyResolvedLocale(program, deps.config);
|
|
24
40
|
const opts = thisCommand.optsWithGlobals();
|
|
25
41
|
if (opts.debugDump) {
|
|
26
42
|
setDebugDump(opts.debugDump);
|
|
@@ -32,44 +48,98 @@ program.hook("preAction", (thisCommand) => {
|
|
|
32
48
|
logger.debug("Logger", "Verbose mode enabled.");
|
|
33
49
|
}
|
|
34
50
|
});
|
|
51
|
+
registerOnboardCommand(program);
|
|
35
52
|
registerAuthCommands(program, deps);
|
|
53
|
+
registerAccountsCommands(program, deps);
|
|
54
|
+
registerMacosCommands(program, deps);
|
|
36
55
|
registerConfigCommands(program, deps);
|
|
37
56
|
registerQueryCommand(program, deps);
|
|
38
57
|
registerApplyCommand(program, deps);
|
|
39
58
|
registerAddCommand(program, deps);
|
|
40
59
|
registerCalCommands(program, deps);
|
|
60
|
+
const ERROR_META_MAX_DETAILS = 800;
|
|
61
|
+
function summarizeForErrorMeta(details) {
|
|
62
|
+
if (details === null || details === undefined)
|
|
63
|
+
return undefined;
|
|
64
|
+
try {
|
|
65
|
+
const s = JSON.stringify(details);
|
|
66
|
+
if (s.length <= ERROR_META_MAX_DETAILS)
|
|
67
|
+
return details;
|
|
68
|
+
return { _truncated: true, preview: s.slice(0, ERROR_META_MAX_DETAILS) };
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return { _nonSerializable: true, string: String(details) };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Single-line JSON for `DEBUG=calendit` diagnostics (not a user-facing i18n key). */
|
|
75
|
+
function logErrorMeta(payload) {
|
|
76
|
+
try {
|
|
77
|
+
logger.debug("ErrorMeta", JSON.stringify(payload));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
logger.debug("ErrorMeta", JSON.stringify({ kind: String(payload.kind ?? "Error") }));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
41
83
|
function handleError(error) {
|
|
42
84
|
const isVerbose = process.argv.includes("--verbose") || process.env.DEBUG === "calendit";
|
|
43
85
|
if (error instanceof ValidationError) {
|
|
44
|
-
logger.error(
|
|
86
|
+
logger.error(`${t("errors.validation.prefix")} ${error.message}`);
|
|
45
87
|
if (error.hint)
|
|
46
|
-
logger.info(
|
|
88
|
+
logger.info(`${t("common.hint")} ${error.hint}`);
|
|
89
|
+
logErrorMeta({
|
|
90
|
+
kind: "ValidationError",
|
|
91
|
+
name: error.name,
|
|
92
|
+
message: error.message,
|
|
93
|
+
hint: error.hint,
|
|
94
|
+
});
|
|
47
95
|
if (isVerbose && error.stack)
|
|
48
96
|
logger.debug("STACK", error.stack);
|
|
49
97
|
process.exit(1);
|
|
50
98
|
}
|
|
51
99
|
if (error instanceof ConfigError) {
|
|
52
|
-
logger.error(
|
|
100
|
+
logger.error(`${t("errors.config.prefix")} ${error.message}`);
|
|
53
101
|
if (error.hint)
|
|
54
|
-
logger.info(
|
|
102
|
+
logger.info(`${t("common.hint")} ${error.hint}`);
|
|
103
|
+
logErrorMeta({
|
|
104
|
+
kind: "ConfigError",
|
|
105
|
+
name: error.name,
|
|
106
|
+
message: error.message,
|
|
107
|
+
hint: error.hint,
|
|
108
|
+
causeCode: error.causeCode ?? null,
|
|
109
|
+
});
|
|
55
110
|
if (isVerbose && error.stack)
|
|
56
111
|
logger.debug("STACK", error.stack);
|
|
57
112
|
process.exit(1);
|
|
58
113
|
}
|
|
59
114
|
if (error instanceof AuthError) {
|
|
60
|
-
logger.error(
|
|
115
|
+
logger.error(`${t("errors.auth.prefix")} ${error.message}`);
|
|
61
116
|
if (error.hint)
|
|
62
|
-
logger.info(
|
|
117
|
+
logger.info(`${t("common.hint")} ${error.hint}`);
|
|
118
|
+
logErrorMeta({
|
|
119
|
+
kind: "AuthError",
|
|
120
|
+
name: error.name,
|
|
121
|
+
message: error.message,
|
|
122
|
+
hint: error.hint,
|
|
123
|
+
});
|
|
63
124
|
if (isVerbose && error.stack)
|
|
64
125
|
logger.debug("STACK", error.stack);
|
|
65
126
|
process.exit(1);
|
|
66
127
|
}
|
|
67
128
|
if (error instanceof ApiError) {
|
|
68
|
-
const statusPart = error.statusCode ?
|
|
69
|
-
|
|
129
|
+
const statusPart = error.statusCode ? String(error.statusCode) : "";
|
|
130
|
+
const prefix = statusPart ? t("errors.apiWithStatus", { status: statusPart }) : `${t("errors.api.prefix")}:`;
|
|
131
|
+
logger.error(`${prefix} ${error.message}`);
|
|
70
132
|
if (error.hint)
|
|
71
|
-
logger.info(
|
|
72
|
-
|
|
133
|
+
logger.info(`${t("common.hint")} ${error.hint}`);
|
|
134
|
+
logErrorMeta({
|
|
135
|
+
kind: "ApiError",
|
|
136
|
+
name: error.name,
|
|
137
|
+
message: error.message,
|
|
138
|
+
hint: error.hint,
|
|
139
|
+
statusCode: error.statusCode ?? null,
|
|
140
|
+
provider: error.provider ?? null,
|
|
141
|
+
details: summarizeForErrorMeta(error.details),
|
|
142
|
+
});
|
|
73
143
|
if (isVerbose && error.stack)
|
|
74
144
|
logger.debug("STACK", error.stack);
|
|
75
145
|
process.exit(1);
|
|
@@ -77,18 +147,33 @@ function handleError(error) {
|
|
|
77
147
|
if (error instanceof CalendarError) {
|
|
78
148
|
logger.error(error.message);
|
|
79
149
|
if (error.hint)
|
|
80
|
-
logger.info(
|
|
150
|
+
logger.info(`${t("common.hint")} ${error.hint}`);
|
|
151
|
+
logErrorMeta({
|
|
152
|
+
kind: "CalendarError",
|
|
153
|
+
name: error.name,
|
|
154
|
+
message: error.message,
|
|
155
|
+
hint: error.hint,
|
|
156
|
+
});
|
|
81
157
|
if (isVerbose && error.stack)
|
|
82
158
|
logger.debug("STACK", error.stack);
|
|
83
159
|
process.exit(1);
|
|
84
160
|
}
|
|
85
161
|
const unknown = error;
|
|
86
|
-
logger.error(
|
|
87
|
-
if (
|
|
88
|
-
|
|
162
|
+
logger.error(`${t("errors.unknown")} ${unknown?.message || String(error)}`);
|
|
163
|
+
if (error instanceof Error) {
|
|
164
|
+
logErrorMeta({
|
|
165
|
+
kind: "UnknownError",
|
|
166
|
+
name: error.name,
|
|
167
|
+
message: error.message,
|
|
168
|
+
});
|
|
169
|
+
if (isVerbose && error.stack)
|
|
170
|
+
logger.debug("STACK", error.stack);
|
|
89
171
|
}
|
|
90
172
|
else {
|
|
91
|
-
|
|
173
|
+
logErrorMeta({
|
|
174
|
+
kind: "UnknownError",
|
|
175
|
+
message: String(error),
|
|
176
|
+
});
|
|
92
177
|
}
|
|
93
178
|
process.exit(1);
|
|
94
179
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
{
|
|
2
|
+
"errors": {
|
|
3
|
+
"validation": { "prefix": "Validation error:" },
|
|
4
|
+
"config": {
|
|
5
|
+
"prefix": "Configuration error:",
|
|
6
|
+
"fileNotFound": "Config file not found.",
|
|
7
|
+
"fileNotFoundHint": "Run `calendit config set-google --id <id> --secret <secret>` (or see docs) to create your configuration.",
|
|
8
|
+
"invalidFormat": "Invalid config file format.",
|
|
9
|
+
"invalidFormatDetails": "Check config.json. Details: {details}",
|
|
10
|
+
"readFailed": "Failed to read configuration.",
|
|
11
|
+
"readFailedHint": "Verify config.json JSON syntax."
|
|
12
|
+
},
|
|
13
|
+
"auth": {
|
|
14
|
+
"prefix": "Authentication error:",
|
|
15
|
+
"googleLoginHint": "See docs/setup_google.md or run `calendit config set-google --file <path>`."
|
|
16
|
+
},
|
|
17
|
+
"api": { "prefix": "API error" },
|
|
18
|
+
"apiWithStatus": "API error ({status}):",
|
|
19
|
+
"unknown": "Unexpected error:",
|
|
20
|
+
"datetime": {
|
|
21
|
+
"invalidFormat": "Invalid date/time format: \"{input}\"",
|
|
22
|
+
"invalidParse": "Could not parse as date/time: \"{input}\"",
|
|
23
|
+
"invalidParseHint": "Check the value. Use HH:mm for times (e.g. today 10:00)."
|
|
24
|
+
},
|
|
25
|
+
"service": {
|
|
26
|
+
"googleCredsNotSet": "Google credentials are not set.",
|
|
27
|
+
"googleCredsNotSetHint": "Run `calendit config set-google --id <id> --secret <secret>`.",
|
|
28
|
+
"outlookCredsNotSet": "Outlook credentials are not set.",
|
|
29
|
+
"outlookCredsNotSetHint": "Run `calendit config set-outlook --id <id>`."
|
|
30
|
+
},
|
|
31
|
+
"context": {
|
|
32
|
+
"missing": "Context '{name}' was not found.",
|
|
33
|
+
"missingHint": "Run `calendit config set-context {name} --service google --calendar primary` to create it.",
|
|
34
|
+
"loadFailed": "Failed to load configuration. Run 'calendit --help' for setup instructions.",
|
|
35
|
+
"loadFailedHint": "Run `calendit config set-google` (etc.) to set up."
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"common": {
|
|
39
|
+
"hint": "Hint:"
|
|
40
|
+
},
|
|
41
|
+
"config": {
|
|
42
|
+
"cmd": {
|
|
43
|
+
"googleSaved": "Google credentials saved to config.",
|
|
44
|
+
"outlookSaved": "Outlook credentials saved to config.",
|
|
45
|
+
"contextSaved": "Context '{name}' saved.",
|
|
46
|
+
"contextDeleted": "Context '{name}' deleted.",
|
|
47
|
+
"localeSet": "UI locale set to '{locale}'.",
|
|
48
|
+
"macosTransportSet": "EventKit default transport is now '{value}' (applies when CALENDIT_EVENTKIT_BRIDGE is not set in the environment)."
|
|
49
|
+
},
|
|
50
|
+
"check": {
|
|
51
|
+
"header": "[CONFIG CHECK]",
|
|
52
|
+
"googleOk": "Google credentials : OK (id: {mask})",
|
|
53
|
+
"googleNotSet": "Google credentials : NOT SET (run: calendit config set-google --id <id> --secret <secret>)",
|
|
54
|
+
"outlookOk": "Outlook credentials: OK (id: {mask})",
|
|
55
|
+
"outlookNotSet": "Outlook credentials: NOT SET (run: calendit config set-outlook --id <id>)",
|
|
56
|
+
"contexts": "Contexts : {list}",
|
|
57
|
+
"contextsNone": "Contexts : none",
|
|
58
|
+
"fileLine": "Config file : ~/.config/calendit/config.json (or CALENDIT_CONFIG_DIR override)",
|
|
59
|
+
"uiLocale": "UI locale : {locale}"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"locale": {
|
|
63
|
+
"bootstrap": {
|
|
64
|
+
"prompt": "Choose your language for calendit messages:",
|
|
65
|
+
"choiceEn": "English",
|
|
66
|
+
"choiceJa": "Japanese (日本語)"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"eventkit": {
|
|
70
|
+
"bridge": {
|
|
71
|
+
"tokenMissing": "EventKit bridge token not found at {path}.",
|
|
72
|
+
"hintStartBridge": "Start the bridge: calendit macos bridge start, or open CalenditEventKitBridge.app (e.g. in /Applications).",
|
|
73
|
+
"connectFailed": "Could not connect to the EventKit bridge at {path}: {message}.",
|
|
74
|
+
"timeout": "EventKit bridge did not respond within {ms} ms.",
|
|
75
|
+
"invalidJson": "Invalid JSON from the EventKit bridge: {line}",
|
|
76
|
+
"closedWithoutResponse": "The EventKit bridge closed before sending a response.",
|
|
77
|
+
"bridgeError": "EventKit bridge error"
|
|
78
|
+
},
|
|
79
|
+
"helper": {
|
|
80
|
+
"missing": "EventKit helper binary not found.",
|
|
81
|
+
"hintBuild": "Set CALENDIT_EVENTKIT_HELPER, or run: (cd native/eventkit-helper && swift build -c release).",
|
|
82
|
+
"timeout": "EventKit helper did not complete within {ms} ms.",
|
|
83
|
+
"exitCode": "EventKit helper exited with code {code}."
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"macos": {
|
|
87
|
+
"bridge": {
|
|
88
|
+
"openFailed": "Could not open the bridge app: {message}.",
|
|
89
|
+
"opened": "Launched {path}. Wait a few seconds, then run: calendit macos doctor",
|
|
90
|
+
"notFound": "CalenditEventKitBridge.app not found. Install a release, use Homebrew when available, or in a full clone run: calendit macos bridge build — then: calendit macos bridge start",
|
|
91
|
+
"buildNoSource": "EventKit bridge sources are not available next to this install. Use a full git clone, or set CALENDIT_EVENTKIT_BRIDGE_ROOT to your native/eventkit-bridge directory.",
|
|
92
|
+
"buildOk": "Build finished. App bundle: {path}",
|
|
93
|
+
"buildFailed": "EventKit bridge build failed: {message}",
|
|
94
|
+
"fetchIntro": "Download the calendit repository snapshot from GitHub, extract only native/eventkit-bridge, and place it under your calendit data directory (next to the bridge token when using the default location). A Swift build (see next step) will compile CalenditEventKitBridge.app.",
|
|
95
|
+
"fetchPlan": "• Source archive: {url}\n• Approx. download size: {sizeLine}\n• Extract to: {dest}\n• Then (optional) run the Swift build to create the .app under .build/ in that folder.",
|
|
96
|
+
"fetchSizeKnown": "about {mb} MiB (Content-Length from server)",
|
|
97
|
+
"fetchSizeUnknown": "unknown (HEAD did not return Content-Length; may be a few MB)",
|
|
98
|
+
"fetchConfirm": "Proceed with download and extract?",
|
|
99
|
+
"fetchCancel": "Canceled. No files were downloaded.",
|
|
100
|
+
"fetchBuildPrompt": "Run the Swift build now to create CalenditEventKitBridge.app? (requires Xcode Command Line Tools: swift, codesign)",
|
|
101
|
+
"fetchOk": "Fetched EventKit bridge sources to {path}. You can run: calendit macos bridge start (after build) or calendit macos setup.",
|
|
102
|
+
"fetchSkipsExisting": "Bridge sources already exist at {path}. Use --force to re-download, or run: calendit macos bridge build",
|
|
103
|
+
"fetchFailed": "Download or extract failed: {message}",
|
|
104
|
+
"fetchNoTty": "Non-interactive: use --yes to confirm the download, or use an interactive terminal."
|
|
105
|
+
},
|
|
106
|
+
"setup": {
|
|
107
|
+
"notDarwin": "macOS only.",
|
|
108
|
+
"noTransport": "EventKit is not available: no helper binary and no ready local bridge. Build the helper, start the EventKit bridge, or see docs.",
|
|
109
|
+
"tcc": "Allow Calendar access for CalenditEventKitBridge: System Settings → Privacy & Security → Calendars. When done, press Enter here to re-check.",
|
|
110
|
+
"pickCalendar": "Which calendar should this context use?",
|
|
111
|
+
"contextName": "Name for this context (e.g. my-mac):",
|
|
112
|
+
"contextNameRequired": "Context name is required.",
|
|
113
|
+
"saved": "Saved context '{name}' (macos, calendar {calendarId}).",
|
|
114
|
+
"noCalendars": "No calendars were returned (or access was denied).",
|
|
115
|
+
"startBridgePrompt": "The bridge does not seem to be running. Open CalenditEventKitBridge.app now?",
|
|
116
|
+
"canceled": "Setup canceled."
|
|
117
|
+
},
|
|
118
|
+
"external": {
|
|
119
|
+
"onlyDarwin": "macOS only.",
|
|
120
|
+
"badSub": "Subcommand must be `doctor`, `list-calendars`, or `shell`.",
|
|
121
|
+
"badSubHint": "Examples: calendit macos external doctor | calendit macos external list-calendars --json | calendit macos external shell",
|
|
122
|
+
"jsonOnlyForList": "`--json` applies only to `list-calendars`.",
|
|
123
|
+
"opened": "Opened Terminal.app — check the new window. Command: {cmd}",
|
|
124
|
+
"osascriptFailed": "Could not open Terminal.app via AppleScript: {message}",
|
|
125
|
+
"suggestionWhenDenied": "Calendar access is denied from this terminal. Run: calendit macos external doctor (permission prompt in Terminal), or calendit macos external shell (open Terminal here for all macos / query commands)."
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|