@zhin.js/plugin-calendar 0.0.0-stage → 0.1.1
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 +189 -2
- package/commands/calendar/auth/index.js +26 -0
- package/commands/calendar/auth/index.ts +29 -0
- package/commands/calendar/logout/index.js +18 -0
- package/commands/calendar/logout/index.ts +18 -0
- package/commands/calendar/status/index.js +10 -0
- package/commands/calendar/status/index.ts +10 -0
- package/lib/caldav.d.ts +18 -0
- package/lib/caldav.d.ts.map +1 -0
- package/lib/caldav.js +167 -0
- package/lib/caldav.js.map +1 -0
- package/lib/chat-auth.d.ts +49 -0
- package/lib/chat-auth.d.ts.map +1 -0
- package/lib/chat-auth.js +233 -0
- package/lib/chat-auth.js.map +1 -0
- package/lib/cli/auth.d.ts +3 -0
- package/lib/cli/auth.d.ts.map +1 -0
- package/lib/cli/auth.js +182 -0
- package/lib/cli/auth.js.map +1 -0
- package/lib/config.d.ts +87 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +95 -0
- package/lib/config.js.map +1 -0
- package/lib/credential-store.d.ts +30 -0
- package/lib/credential-store.d.ts.map +1 -0
- package/lib/credential-store.js +42 -0
- package/lib/credential-store.js.map +1 -0
- package/lib/free-time.d.ts +13 -0
- package/lib/free-time.d.ts.map +1 -0
- package/lib/free-time.js +96 -0
- package/lib/free-time.js.map +1 -0
- package/lib/http-callback.d.ts +13 -0
- package/lib/http-callback.d.ts.map +1 -0
- package/lib/http-callback.js +26 -0
- package/lib/http-callback.js.map +1 -0
- package/lib/index.d.ts +32 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +122 -0
- package/lib/index.js.map +1 -0
- package/lib/oauth-auth.d.ts +37 -0
- package/lib/oauth-auth.d.ts.map +1 -0
- package/lib/oauth-auth.js +91 -0
- package/lib/oauth-auth.js.map +1 -0
- package/lib/oauth-session.d.ts +27 -0
- package/lib/oauth-session.d.ts.map +1 -0
- package/lib/oauth-session.js +43 -0
- package/lib/oauth-session.js.map +1 -0
- package/lib/oauth.d.ts +28 -0
- package/lib/oauth.d.ts.map +1 -0
- package/lib/oauth.js +200 -0
- package/lib/oauth.js.map +1 -0
- package/lib/runtime.d.ts +11 -0
- package/lib/runtime.d.ts.map +1 -0
- package/lib/runtime.js +3 -0
- package/lib/runtime.js.map +1 -0
- package/lib/secrets.d.ts +3 -0
- package/lib/secrets.d.ts.map +1 -0
- package/lib/secrets.js +6 -0
- package/lib/secrets.js.map +1 -0
- package/lib/time.d.ts +37 -0
- package/lib/time.d.ts.map +1 -0
- package/lib/time.js +118 -0
- package/lib/time.js.map +1 -0
- package/lib/token-manager.d.ts +71 -0
- package/lib/token-manager.d.ts.map +1 -0
- package/lib/token-manager.js +239 -0
- package/lib/token-manager.js.map +1 -0
- package/lib/tool-errors.d.ts +6 -0
- package/lib/tool-errors.d.ts.map +1 -0
- package/lib/tool-errors.js +17 -0
- package/lib/tool-errors.js.map +1 -0
- package/lib/types.d.ts +28 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/package.json +112 -3
- package/plugin.js +72 -0
- package/skills/calendar-scheduling/SKILL.md +38 -0
- package/skills/calendar-scheduling/tools/create_event/index.js +38 -0
- package/skills/calendar-scheduling/tools/create_event/index.ts +41 -0
- package/skills/calendar-scheduling/tools/find_free_time/index.js +46 -0
- package/skills/calendar-scheduling/tools/find_free_time/index.ts +50 -0
- package/skills/calendar-scheduling/tools/list_events/index.js +32 -0
- package/skills/calendar-scheduling/tools/list_events/index.ts +42 -0
- package/src/caldav.ts +198 -0
- package/src/chat-auth.ts +336 -0
- package/src/cli/auth.ts +235 -0
- package/src/config.ts +108 -0
- package/src/credential-store.ts +79 -0
- package/src/free-time.ts +128 -0
- package/src/http-callback.ts +55 -0
- package/src/index.ts +193 -0
- package/src/oauth-auth.ts +145 -0
- package/src/oauth-session.ts +65 -0
- package/src/oauth.ts +298 -0
- package/src/runtime.ts +16 -0
- package/src/secrets.ts +5 -0
- package/src/time.ts +167 -0
- package/src/token-manager.ts +358 -0
- package/src/tool-errors.ts +18 -0
- package/src/types.ts +37 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persists OAuth refresh tokens in the plugin's database state (never in .env).
|
|
3
|
+
*/
|
|
4
|
+
import type { OAuthProviderKind, RefreshTokenStore } from "./token-manager.js";
|
|
5
|
+
/** The subset of zhin's PluginStateStore used by the calendar plugin. */
|
|
6
|
+
export interface CalendarStateStore {
|
|
7
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
8
|
+
set(key: string, value: never): Promise<void>;
|
|
9
|
+
delete(key: string): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
/** Who completed the chat authorization and where to notify them later. */
|
|
12
|
+
export interface CredentialOwner<Target = unknown> {
|
|
13
|
+
readonly sender_id: string;
|
|
14
|
+
readonly target: Target;
|
|
15
|
+
}
|
|
16
|
+
export interface StoredCredential<Target = unknown> {
|
|
17
|
+
readonly refresh_token: string;
|
|
18
|
+
readonly authorized_at: string;
|
|
19
|
+
readonly authorized_by: CredentialOwner<Target> | null;
|
|
20
|
+
}
|
|
21
|
+
export declare class CalendarCredentialStore {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(state: CalendarStateStore);
|
|
24
|
+
get(provider: OAuthProviderKind): Promise<StoredCredential | undefined>;
|
|
25
|
+
save(provider: OAuthProviderKind, refreshToken: string, authorizedBy: CredentialOwner | null): Promise<void>;
|
|
26
|
+
delete(provider: OAuthProviderKind): Promise<void>;
|
|
27
|
+
/** Adapter for OAuthTokenManager: loads and saves only the refresh token. */
|
|
28
|
+
refreshTokenStore(provider: OAuthProviderKind): RefreshTokenStore;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=credential-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-store.d.ts","sourceRoot":"","sources":["../src/credential-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE/E,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC5C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe,CAAC,MAAM,GAAG,OAAO;IAC/C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB,CAAC,MAAM,GAAG,OAAO;IAChD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CACxD;AAMD,qBAAa,uBAAuB;;gBAGtB,KAAK,EAAE,kBAAkB;IAI/B,GAAG,CACP,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAUlC,IAAI,CACR,QAAQ,EAAE,iBAAiB,EAC3B,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,eAAe,GAAG,IAAI,GACnC,OAAO,CAAC,IAAI,CAAC;IASV,MAAM,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxD,6EAA6E;IAC7E,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,iBAAiB;CAalE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function credentialKey(provider) {
|
|
2
|
+
return `oauth/${provider}`;
|
|
3
|
+
}
|
|
4
|
+
export class CalendarCredentialStore {
|
|
5
|
+
#state;
|
|
6
|
+
constructor(state) {
|
|
7
|
+
this.#state = state;
|
|
8
|
+
}
|
|
9
|
+
async get(provider) {
|
|
10
|
+
const stored = await this.#state.get(credentialKey(provider));
|
|
11
|
+
if (!stored || typeof stored.refresh_token !== "string") {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
return stored;
|
|
15
|
+
}
|
|
16
|
+
async save(provider, refreshToken, authorizedBy) {
|
|
17
|
+
const credential = {
|
|
18
|
+
refresh_token: refreshToken,
|
|
19
|
+
authorized_at: new Date().toISOString(),
|
|
20
|
+
authorized_by: authorizedBy,
|
|
21
|
+
};
|
|
22
|
+
await this.#state.set(credentialKey(provider), credential);
|
|
23
|
+
}
|
|
24
|
+
async delete(provider) {
|
|
25
|
+
await this.#state.delete(credentialKey(provider));
|
|
26
|
+
}
|
|
27
|
+
/** Adapter for OAuthTokenManager: loads and saves only the refresh token. */
|
|
28
|
+
refreshTokenStore(provider) {
|
|
29
|
+
return {
|
|
30
|
+
load: async () => {
|
|
31
|
+
const credential = await this.get(provider);
|
|
32
|
+
return credential?.refresh_token;
|
|
33
|
+
},
|
|
34
|
+
save: async (refreshToken) => {
|
|
35
|
+
const existing = await this.get(provider);
|
|
36
|
+
const authorizedBy = existing?.authorized_by ?? null;
|
|
37
|
+
await this.save(provider, refreshToken, authorizedBy);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=credential-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-store.js","sourceRoot":"","sources":["../src/credential-store.ts"],"names":[],"mappings":"AAwBA,SAAS,aAAa,CAAC,QAA2B;IAChD,OAAO,SAAS,QAAQ,EAAE,CAAC;AAC7B,CAAC;AAED,MAAM,OAAO,uBAAuB;IACzB,MAAM,CAAqB;IAEpC,YAAY,KAAyB;QACnC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,GAAG,CACP,QAA2B;QAE3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAClC,aAAa,CAAC,QAAQ,CAAC,CACxB,CAAC;QACF,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,aAAa,KAAK,QAAQ,EAAE,CAAC;YACxD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,IAAI,CACR,QAA2B,EAC3B,YAAoB,EACpB,YAAoC;QAEpC,MAAM,UAAU,GAAqB;YACnC,aAAa,EAAE,YAAY;YAC3B,aAAa,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACvC,aAAa,EAAE,YAAY;SAC5B,CAAC;QACF,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,UAAmB,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,QAA2B;QACtC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,6EAA6E;IAC7E,iBAAiB,CAAC,QAA2B;QAC3C,OAAO;YACL,IAAI,EAAE,KAAK,IAAI,EAAE;gBACf,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBAC5C,OAAO,UAAU,EAAE,aAAa,CAAC;YACnC,CAAC;YACD,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE;gBAC3B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBAC1C,MAAM,YAAY,GAAG,QAAQ,EAAE,aAAa,IAAI,IAAI,CAAC;gBACrD,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;YACxD,CAAC;SACF,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CalendarEvent, FreeSlot } from "./types.js";
|
|
2
|
+
export interface FreeTimeOptions {
|
|
3
|
+
readonly from: Date;
|
|
4
|
+
readonly to: Date;
|
|
5
|
+
readonly durationMinutes: number;
|
|
6
|
+
readonly workDayStart: string;
|
|
7
|
+
readonly workDayEnd: string;
|
|
8
|
+
readonly timezone: string;
|
|
9
|
+
readonly events: readonly CalendarEvent[];
|
|
10
|
+
}
|
|
11
|
+
/** Returns free slots of at least `durationMinutes`, clipped to work hours. */
|
|
12
|
+
export declare function findFreeSlots(options: FreeTimeOptions): FreeSlot[];
|
|
13
|
+
//# sourceMappingURL=free-time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"free-time.d.ts","sourceRoot":"","sources":["../src/free-time.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;CAC3C;AAuBD,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,EAAE,CAoFlE"}
|
package/lib/free-time.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds free slots inside work hours between `from` and `to`, given busy events.
|
|
3
|
+
*/
|
|
4
|
+
import { formatInZone, parseDateTimeInZone, zonedParts, zonedTimeToUtc, } from "./time.js";
|
|
5
|
+
function parseHm(value) {
|
|
6
|
+
const match = /^(\d{1,2}):(\d{2})$/.exec(value.trim());
|
|
7
|
+
if (!match)
|
|
8
|
+
throw new Error(`工作时间格式无效:${value}(示例:09:00)`);
|
|
9
|
+
return { hour: Number(match[1]), minute: Number(match[2]) };
|
|
10
|
+
}
|
|
11
|
+
/** Advance `instant` by one calendar day in `timeZone` (noon-based to avoid DST edges). */
|
|
12
|
+
function nextCalendarDay(instant, timeZone) {
|
|
13
|
+
const parts = zonedParts(instant, timeZone);
|
|
14
|
+
return zonedTimeToUtc({
|
|
15
|
+
year: parts.year,
|
|
16
|
+
month: parts.month,
|
|
17
|
+
day: parts.day + 1,
|
|
18
|
+
hour: 12,
|
|
19
|
+
minute: 0,
|
|
20
|
+
}, timeZone);
|
|
21
|
+
}
|
|
22
|
+
/** Returns free slots of at least `durationMinutes`, clipped to work hours. */
|
|
23
|
+
export function findFreeSlots(options) {
|
|
24
|
+
const startHm = parseHm(options.workDayStart);
|
|
25
|
+
const endHm = parseHm(options.workDayEnd);
|
|
26
|
+
const durationMs = options.durationMinutes * 60000;
|
|
27
|
+
if (durationMs <= 0)
|
|
28
|
+
throw new Error("时长必须大于 0 分钟");
|
|
29
|
+
const busy = options.events
|
|
30
|
+
.map((event) => ({
|
|
31
|
+
start: parseDateTimeInZone(event.start, options.timezone).getTime(),
|
|
32
|
+
end: parseDateTimeInZone(event.end, options.timezone).getTime(),
|
|
33
|
+
}))
|
|
34
|
+
.filter((interval) => Number.isFinite(interval.start) && Number.isFinite(interval.end))
|
|
35
|
+
.sort((a, b) => a.start - b.start);
|
|
36
|
+
const slots = [];
|
|
37
|
+
// Walk calendar days via noon anchors so dayEnd+1min cannot stall the loop.
|
|
38
|
+
let cursorNoon = zonedTimeToUtc((() => {
|
|
39
|
+
const parts = zonedParts(options.from, options.timezone);
|
|
40
|
+
return {
|
|
41
|
+
year: parts.year,
|
|
42
|
+
month: parts.month,
|
|
43
|
+
day: parts.day,
|
|
44
|
+
hour: 12,
|
|
45
|
+
minute: 0,
|
|
46
|
+
};
|
|
47
|
+
})(), options.timezone);
|
|
48
|
+
const toMs = options.to.getTime();
|
|
49
|
+
let guard = 0;
|
|
50
|
+
while (cursorNoon.getTime() < toMs + 24 * 3600 * 1000 && guard < 400) {
|
|
51
|
+
guard += 1;
|
|
52
|
+
const parts = zonedParts(cursorNoon, options.timezone);
|
|
53
|
+
const dayStart = zonedTimeToUtc({
|
|
54
|
+
year: parts.year,
|
|
55
|
+
month: parts.month,
|
|
56
|
+
day: parts.day,
|
|
57
|
+
hour: startHm.hour,
|
|
58
|
+
minute: startHm.minute,
|
|
59
|
+
}, options.timezone).getTime();
|
|
60
|
+
const dayEnd = zonedTimeToUtc({
|
|
61
|
+
year: parts.year,
|
|
62
|
+
month: parts.month,
|
|
63
|
+
day: parts.day,
|
|
64
|
+
hour: endHm.hour,
|
|
65
|
+
minute: endHm.minute,
|
|
66
|
+
}, options.timezone).getTime();
|
|
67
|
+
if (dayEnd <= options.from.getTime()) {
|
|
68
|
+
cursorNoon = nextCalendarDay(cursorNoon, options.timezone);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (dayStart >= toMs)
|
|
72
|
+
break;
|
|
73
|
+
let freeStart = Math.max(dayStart, options.from.getTime());
|
|
74
|
+
const freeEndLimit = Math.min(dayEnd, toMs);
|
|
75
|
+
for (const interval of busy) {
|
|
76
|
+
if (interval.end <= freeStart || interval.start >= freeEndLimit)
|
|
77
|
+
continue;
|
|
78
|
+
if (interval.start - freeStart >= durationMs) {
|
|
79
|
+
slots.push({
|
|
80
|
+
start: formatInZone(new Date(freeStart), options.timezone),
|
|
81
|
+
end: formatInZone(new Date(interval.start), options.timezone),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
freeStart = Math.max(freeStart, interval.end);
|
|
85
|
+
}
|
|
86
|
+
if (freeEndLimit - freeStart >= durationMs) {
|
|
87
|
+
slots.push({
|
|
88
|
+
start: formatInZone(new Date(freeStart), options.timezone),
|
|
89
|
+
end: formatInZone(new Date(freeEndLimit), options.timezone),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
cursorNoon = nextCalendarDay(cursorNoon, options.timezone);
|
|
93
|
+
}
|
|
94
|
+
return slots;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=free-time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"free-time.js","sourceRoot":"","sources":["../src/free-time.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth redirect endpoint mounted on zhin's shared HTTP host.
|
|
3
|
+
*/
|
|
4
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
5
|
+
import type { CallbackResult } from "./chat-auth.js";
|
|
6
|
+
export interface CalendarRouteHost {
|
|
7
|
+
route(method: string, path: string, handler: (request: IncomingMessage, response: ServerResponse, url: URL) => void | Promise<void>, meta?: {
|
|
8
|
+
readonly summary?: string;
|
|
9
|
+
readonly tags?: readonly string[];
|
|
10
|
+
}): () => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function registerOAuthCallbackRoute(http: CalendarRouteHost, callbackPath: string, handleCallback: (searchParams: URLSearchParams) => Promise<CallbackResult>): () => void;
|
|
13
|
+
//# sourceMappingURL=http-callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-callback.d.ts","sourceRoot":"","sources":["../src/http-callback.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CACH,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,CACP,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,cAAc,EACxB,GAAG,EAAE,GAAG,KACL,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACzB,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GACtE,MAAM,IAAI,CAAC;CACf;AAmBD,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,iBAAiB,EACvB,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,CAAC,YAAY,EAAE,eAAe,KAAK,OAAO,CAAC,cAAc,CAAC,GACzE,MAAM,IAAI,CAcZ"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function escapeHtml(text) {
|
|
2
|
+
return text
|
|
3
|
+
.replaceAll("&", "&")
|
|
4
|
+
.replaceAll("<", "<")
|
|
5
|
+
.replaceAll(">", ">")
|
|
6
|
+
.replaceAll('"', """);
|
|
7
|
+
}
|
|
8
|
+
function renderPage(result) {
|
|
9
|
+
return [
|
|
10
|
+
"<!doctype html>",
|
|
11
|
+
'<meta charset="utf-8">',
|
|
12
|
+
"<title>zhin calendar</title>",
|
|
13
|
+
`<p>${escapeHtml(result.message)}</p>`,
|
|
14
|
+
].join("\n");
|
|
15
|
+
}
|
|
16
|
+
export function registerOAuthCallbackRoute(http, callbackPath, handleCallback) {
|
|
17
|
+
return http.route("GET", callbackPath, async (_request, response, url) => {
|
|
18
|
+
const result = await handleCallback(url.searchParams);
|
|
19
|
+
response.writeHead(result.statusCode, {
|
|
20
|
+
"content-type": "text/html; charset=utf-8",
|
|
21
|
+
"cache-control": "no-store",
|
|
22
|
+
});
|
|
23
|
+
response.end(renderPage(result));
|
|
24
|
+
}, { summary: "Calendar OAuth callback", tags: ["calendar"] });
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=http-callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-callback.js","sourceRoot":"","sources":["../src/http-callback.ts"],"names":[],"mappings":"AAmBA,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI;SACR,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,UAAU,CAAC,MAAsB;IACxC,OAAO;QACL,iBAAiB;QACjB,wBAAwB;QACxB,8BAA8B;QAC9B,MAAM,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM;KACvC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,IAAuB,EACvB,YAAoB,EACpB,cAA0E;IAE1E,OAAO,IAAI,CAAC,KAAK,CACf,KAAK,EACL,YAAY,EACZ,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE;QAChC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACtD,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,EAAE;YACpC,cAAc,EAAE,0BAA0B;YAC1C,eAAe,EAAE,UAAU;SAC5B,CAAC,CAAC;QACH,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IACnC,CAAC,EACD,EAAE,OAAO,EAAE,yBAAyB,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,CAC3D,CAAC;AACJ,CAAC"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CalendarConfig } from "./config.js";
|
|
2
|
+
import type { CalendarProvider } from "./types.js";
|
|
3
|
+
import type { CalendarCredentialStore } from "./credential-store.js";
|
|
4
|
+
import { OAuthTokenManager, type AuthorizationLostListener, type OAuthProviderKind } from "./token-manager.js";
|
|
5
|
+
export type { CalendarEvent, CreateEventInput, FreeSlot, CalendarProvider, } from "./types.js";
|
|
6
|
+
export { findFreeSlots } from "./free-time.js";
|
|
7
|
+
export { parseCalDavEvents } from "./caldav.js";
|
|
8
|
+
export { calendarConfig, assertCalendarTimezone, type CalendarConfig, } from "./config.js";
|
|
9
|
+
export { cleanSecret } from "./secrets.js";
|
|
10
|
+
export { calendarRuntimeToken, type CalendarRuntime } from "./runtime.js";
|
|
11
|
+
export { OAuthTokenManager, OAuthTokenError, authCommandFor, type OAuthProviderKind, type RefreshTokenStore, type TokenManagerOptions, type TokenSet, } from "./token-manager.js";
|
|
12
|
+
export { CalendarCredentialStore, type CalendarStateStore, type CredentialOwner, type StoredCredential, } from "./credential-store.js";
|
|
13
|
+
export { CalendarAuthService, DEFAULT_OAUTH_CALLBACK_PATH, buildRedirectUri, localOriginFromAddress, parseOAuthProvider, } from "./chat-auth.js";
|
|
14
|
+
export { OAuthSessionRegistry, OAUTH_SESSION_TTL_MS } from "./oauth-session.js";
|
|
15
|
+
export { buildAuthorizeUrl, createPkcePair, createOAuthState, exchangeAuthorizationCode, GOOGLE_AUTH_SCOPES, MICROSOFT_AUTH_SCOPES, } from "./oauth-auth.js";
|
|
16
|
+
export { microsoftCalendarBase } from "./oauth.js";
|
|
17
|
+
export interface CalendarProviderDependencies {
|
|
18
|
+
/** Token managers shared with the chat authorization flow. */
|
|
19
|
+
readonly managers?: Partial<Record<OAuthProviderKind, OAuthTokenManager>>;
|
|
20
|
+
}
|
|
21
|
+
export interface TokenManagerDependencies {
|
|
22
|
+
readonly credentials?: CalendarCredentialStore;
|
|
23
|
+
readonly onAuthorizationLost?: AuthorizationLostListener;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates one token manager per OAuth provider that has client credentials.
|
|
27
|
+
* Stored (chat-authorized) refresh tokens win over config/env values.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createOAuthTokenManagers(config: CalendarConfig, dependencies?: TokenManagerDependencies): Partial<Record<OAuthProviderKind, OAuthTokenManager>>;
|
|
30
|
+
/** Builds the configured provider, or `undefined` when none is enabled. */
|
|
31
|
+
export declare function createCalendarProvider(config: CalendarConfig, dependencies?: CalendarProviderDependencies): CalendarProvider | undefined;
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAOlD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,QAAQ,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,uBAAuB,EACvB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,yBAAyB,EACzB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,4BAA4B;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAC,CAAC;CAC3E;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,WAAW,CAAC,EAAE,uBAAuB,CAAC;IAC/C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,yBAAyB,CAAC;CAC1D;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,cAAc,EACtB,YAAY,GAAE,wBAA6B,GAC1C,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAC,CAiCvD;AAED,2EAA2E;AAC3E,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,cAAc,EACtB,YAAY,GAAE,4BAAiC,GAC9C,gBAAgB,GAAG,SAAS,CAyE9B"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { cleanSecret } from "./secrets.js";
|
|
2
|
+
import { createCalDavProvider } from "./caldav.js";
|
|
3
|
+
import { createGoogleCalendarProvider, createMicrosoftCalendarProvider, } from "./oauth.js";
|
|
4
|
+
import { OAuthTokenManager, } from "./token-manager.js";
|
|
5
|
+
export { findFreeSlots } from "./free-time.js";
|
|
6
|
+
export { parseCalDavEvents } from "./caldav.js";
|
|
7
|
+
export { calendarConfig, assertCalendarTimezone, } from "./config.js";
|
|
8
|
+
export { cleanSecret } from "./secrets.js";
|
|
9
|
+
export { calendarRuntimeToken } from "./runtime.js";
|
|
10
|
+
export { OAuthTokenManager, OAuthTokenError, authCommandFor, } from "./token-manager.js";
|
|
11
|
+
export { CalendarCredentialStore, } from "./credential-store.js";
|
|
12
|
+
export { CalendarAuthService, DEFAULT_OAUTH_CALLBACK_PATH, buildRedirectUri, localOriginFromAddress, parseOAuthProvider, } from "./chat-auth.js";
|
|
13
|
+
export { OAuthSessionRegistry, OAUTH_SESSION_TTL_MS } from "./oauth-session.js";
|
|
14
|
+
export { buildAuthorizeUrl, createPkcePair, createOAuthState, exchangeAuthorizationCode, GOOGLE_AUTH_SCOPES, MICROSOFT_AUTH_SCOPES, } from "./oauth-auth.js";
|
|
15
|
+
export { microsoftCalendarBase } from "./oauth.js";
|
|
16
|
+
/**
|
|
17
|
+
* Creates one token manager per OAuth provider that has client credentials.
|
|
18
|
+
* Stored (chat-authorized) refresh tokens win over config/env values.
|
|
19
|
+
*/
|
|
20
|
+
export function createOAuthTokenManagers(config, dependencies = {}) {
|
|
21
|
+
const managers = {};
|
|
22
|
+
const google = config.google;
|
|
23
|
+
const microsoft = config.microsoft;
|
|
24
|
+
const googleClientId = cleanSecret(google?.client_id ?? "");
|
|
25
|
+
const googleClientSecret = cleanSecret(google?.client_secret ?? "");
|
|
26
|
+
if (googleClientId && googleClientSecret) {
|
|
27
|
+
managers.google = new OAuthTokenManager({
|
|
28
|
+
provider: "google",
|
|
29
|
+
clientId: googleClientId,
|
|
30
|
+
clientSecret: googleClientSecret,
|
|
31
|
+
initialRefreshToken: cleanSecret(google?.refresh_token ?? ""),
|
|
32
|
+
tokenFile: cleanSecret(google?.token_file ?? "") || undefined,
|
|
33
|
+
tokenStore: dependencies.credentials?.refreshTokenStore("google"),
|
|
34
|
+
onAuthorizationLost: dependencies.onAuthorizationLost,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const microsoftClientId = cleanSecret(microsoft?.client_id ?? "");
|
|
38
|
+
if (microsoftClientId) {
|
|
39
|
+
managers.microsoft = new OAuthTokenManager({
|
|
40
|
+
provider: "microsoft",
|
|
41
|
+
clientId: microsoftClientId,
|
|
42
|
+
clientSecret: cleanSecret(microsoft?.client_secret ?? "") || undefined,
|
|
43
|
+
tenant: microsoft?.tenant || "common",
|
|
44
|
+
initialRefreshToken: cleanSecret(microsoft?.refresh_token ?? ""),
|
|
45
|
+
tokenFile: cleanSecret(microsoft?.token_file ?? "") || undefined,
|
|
46
|
+
tokenStore: dependencies.credentials?.refreshTokenStore("microsoft"),
|
|
47
|
+
onAuthorizationLost: dependencies.onAuthorizationLost,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return managers;
|
|
51
|
+
}
|
|
52
|
+
/** Builds the configured provider, or `undefined` when none is enabled. */
|
|
53
|
+
export function createCalendarProvider(config, dependencies = {}) {
|
|
54
|
+
const caldav = config.caldav;
|
|
55
|
+
const google = config.google;
|
|
56
|
+
const microsoft = config.microsoft;
|
|
57
|
+
const managers = dependencies.managers;
|
|
58
|
+
if (config.provider === "caldav" &&
|
|
59
|
+
caldav?.enabled &&
|
|
60
|
+
cleanSecret(caldav.calendar_url ?? "")) {
|
|
61
|
+
return createCalDavProvider({
|
|
62
|
+
calendarUrl: cleanSecret(caldav.calendar_url ?? ""),
|
|
63
|
+
username: cleanSecret(caldav.username ?? ""),
|
|
64
|
+
password: cleanSecret(caldav.password ?? ""),
|
|
65
|
+
timezone: config.timezone,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (config.provider === "google" && google?.enabled) {
|
|
69
|
+
const calendarId = (google.calendar_id || "primary").trim() || "primary";
|
|
70
|
+
const sharedManager = managers?.google;
|
|
71
|
+
if (sharedManager) {
|
|
72
|
+
return createGoogleCalendarProvider({
|
|
73
|
+
clientId: cleanSecret(google.client_id ?? ""),
|
|
74
|
+
tokenManager: sharedManager,
|
|
75
|
+
calendarId,
|
|
76
|
+
timezone: config.timezone,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const clientId = cleanSecret(google.client_id ?? "");
|
|
80
|
+
const clientSecret = cleanSecret(google.client_secret ?? "");
|
|
81
|
+
const refreshToken = cleanSecret(google.refresh_token ?? "");
|
|
82
|
+
if (!clientId || !clientSecret || !refreshToken) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
return createGoogleCalendarProvider({
|
|
86
|
+
clientId,
|
|
87
|
+
clientSecret,
|
|
88
|
+
refreshToken,
|
|
89
|
+
calendarId,
|
|
90
|
+
timezone: config.timezone,
|
|
91
|
+
tokenFile: cleanSecret(google.token_file ?? "") || undefined,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (config.provider === "microsoft" && microsoft?.enabled) {
|
|
95
|
+
const calendarId = (microsoft.calendar_id ?? "").trim();
|
|
96
|
+
const sharedManager = managers?.microsoft;
|
|
97
|
+
if (sharedManager) {
|
|
98
|
+
return createMicrosoftCalendarProvider({
|
|
99
|
+
clientId: cleanSecret(microsoft.client_id ?? ""),
|
|
100
|
+
tokenManager: sharedManager,
|
|
101
|
+
calendarId,
|
|
102
|
+
timezone: config.timezone,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const clientId = cleanSecret(microsoft.client_id ?? "");
|
|
106
|
+
const refreshToken = cleanSecret(microsoft.refresh_token ?? "");
|
|
107
|
+
if (!clientId || !refreshToken) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
return createMicrosoftCalendarProvider({
|
|
111
|
+
clientId,
|
|
112
|
+
clientSecret: cleanSecret(microsoft.client_secret ?? "") || undefined,
|
|
113
|
+
refreshToken,
|
|
114
|
+
calendarId,
|
|
115
|
+
timezone: config.timezone,
|
|
116
|
+
tenant: microsoft.tenant || "common",
|
|
117
|
+
tokenFile: cleanSecret(microsoft.token_file ?? "") || undefined,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare const GOOGLE_AUTH_SCOPES: readonly ["https://www.googleapis.com/auth/calendar.events", "https://www.googleapis.com/auth/calendar.readonly"];
|
|
2
|
+
export declare const MICROSOFT_AUTH_SCOPES: readonly ["offline_access", "Calendars.ReadWrite", "User.Read"];
|
|
3
|
+
export interface PkcePair {
|
|
4
|
+
readonly verifier: string;
|
|
5
|
+
readonly challenge: string;
|
|
6
|
+
}
|
|
7
|
+
/** RFC 7636 S256 PKCE pair. */
|
|
8
|
+
export declare function createPkcePair(random?: () => Buffer): PkcePair;
|
|
9
|
+
export declare function createOAuthState(random?: () => Buffer): string;
|
|
10
|
+
export interface AuthUrlOptions {
|
|
11
|
+
readonly provider: "google" | "microsoft";
|
|
12
|
+
readonly clientId: string;
|
|
13
|
+
readonly redirectUri: string;
|
|
14
|
+
readonly state: string;
|
|
15
|
+
readonly codeChallenge: string;
|
|
16
|
+
readonly tenant?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Builds the browser authorize URL (PKCE S256). */
|
|
19
|
+
export declare function buildAuthorizeUrl(options: AuthUrlOptions): string;
|
|
20
|
+
export interface ExchangeOptions {
|
|
21
|
+
readonly provider: "google" | "microsoft";
|
|
22
|
+
readonly clientId: string;
|
|
23
|
+
readonly clientSecret?: string;
|
|
24
|
+
readonly tenant?: string;
|
|
25
|
+
readonly redirectUri: string;
|
|
26
|
+
readonly code: string;
|
|
27
|
+
readonly codeVerifier: string;
|
|
28
|
+
readonly fetchImpl?: typeof fetch;
|
|
29
|
+
}
|
|
30
|
+
export interface ExchangedTokens {
|
|
31
|
+
readonly accessToken: string;
|
|
32
|
+
readonly refreshToken: string;
|
|
33
|
+
readonly expiresIn: number;
|
|
34
|
+
}
|
|
35
|
+
/** Exchanges an authorization code for tokens (PKCE). */
|
|
36
|
+
export declare function exchangeAuthorizationCode(options: ExchangeOptions): Promise<ExchangedTokens>;
|
|
37
|
+
//# sourceMappingURL=oauth-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-auth.d.ts","sourceRoot":"","sources":["../src/oauth-auth.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,kBAAkB,mHAGrB,CAAC;AAEX,eAAO,MAAM,qBAAqB,iEAIxB,CAAC;AAEX,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,+BAA+B;AAC/B,wBAAgB,cAAc,CAC5B,MAAM,GAAE,MAAM,MAA8B,GAC3C,QAAQ,CAIV;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,GAAE,MAAM,MAA8B,GAC3C,MAAM,CAER;AAMD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,oDAAoD;AACpD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CA2BjE;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,yDAAyD;AACzD,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,eAAe,CAAC,CA6C1B"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth authorization-code + PKCE helpers for the one-time auth CLI.
|
|
3
|
+
*/
|
|
4
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
5
|
+
export const GOOGLE_AUTH_SCOPES = [
|
|
6
|
+
"https://www.googleapis.com/auth/calendar.events",
|
|
7
|
+
"https://www.googleapis.com/auth/calendar.readonly",
|
|
8
|
+
];
|
|
9
|
+
export const MICROSOFT_AUTH_SCOPES = [
|
|
10
|
+
"offline_access",
|
|
11
|
+
"Calendars.ReadWrite",
|
|
12
|
+
"User.Read",
|
|
13
|
+
];
|
|
14
|
+
/** RFC 7636 S256 PKCE pair. */
|
|
15
|
+
export function createPkcePair(random = () => randomBytes(32)) {
|
|
16
|
+
const verifier = base64Url(random());
|
|
17
|
+
const challenge = base64Url(createHash("sha256").update(verifier).digest());
|
|
18
|
+
return { verifier, challenge };
|
|
19
|
+
}
|
|
20
|
+
export function createOAuthState(random = () => randomBytes(16)) {
|
|
21
|
+
return base64Url(random());
|
|
22
|
+
}
|
|
23
|
+
function base64Url(buffer) {
|
|
24
|
+
return buffer.toString("base64url");
|
|
25
|
+
}
|
|
26
|
+
/** Builds the browser authorize URL (PKCE S256). */
|
|
27
|
+
export function buildAuthorizeUrl(options) {
|
|
28
|
+
if (options.provider === "google") {
|
|
29
|
+
const url = new URL("https://accounts.google.com/o/oauth2/v2/auth");
|
|
30
|
+
url.searchParams.set("client_id", options.clientId);
|
|
31
|
+
url.searchParams.set("redirect_uri", options.redirectUri);
|
|
32
|
+
url.searchParams.set("response_type", "code");
|
|
33
|
+
url.searchParams.set("scope", GOOGLE_AUTH_SCOPES.join(" "));
|
|
34
|
+
url.searchParams.set("access_type", "offline");
|
|
35
|
+
url.searchParams.set("prompt", "consent");
|
|
36
|
+
url.searchParams.set("state", options.state);
|
|
37
|
+
url.searchParams.set("code_challenge", options.codeChallenge);
|
|
38
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
39
|
+
return url.toString();
|
|
40
|
+
}
|
|
41
|
+
const tenant = options.tenant?.trim() || "common";
|
|
42
|
+
const url = new URL(`https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize`);
|
|
43
|
+
url.searchParams.set("client_id", options.clientId);
|
|
44
|
+
url.searchParams.set("redirect_uri", options.redirectUri);
|
|
45
|
+
url.searchParams.set("response_type", "code");
|
|
46
|
+
url.searchParams.set("response_mode", "query");
|
|
47
|
+
url.searchParams.set("scope", MICROSOFT_AUTH_SCOPES.join(" "));
|
|
48
|
+
url.searchParams.set("state", options.state);
|
|
49
|
+
url.searchParams.set("code_challenge", options.codeChallenge);
|
|
50
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
51
|
+
return url.toString();
|
|
52
|
+
}
|
|
53
|
+
/** Exchanges an authorization code for tokens (PKCE). */
|
|
54
|
+
export async function exchangeAuthorizationCode(options) {
|
|
55
|
+
const body = new URLSearchParams({
|
|
56
|
+
client_id: options.clientId,
|
|
57
|
+
code: options.code,
|
|
58
|
+
redirect_uri: options.redirectUri,
|
|
59
|
+
grant_type: "authorization_code",
|
|
60
|
+
code_verifier: options.codeVerifier,
|
|
61
|
+
});
|
|
62
|
+
if (options.clientSecret?.trim())
|
|
63
|
+
body.set("client_secret", options.clientSecret.trim());
|
|
64
|
+
const url = options.provider === "google"
|
|
65
|
+
? "https://oauth2.googleapis.com/token"
|
|
66
|
+
: `https://login.microsoftonline.com/${options.tenant?.trim() || "common"}/oauth2/v2.0/token`;
|
|
67
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
68
|
+
const response = await fetchImpl(url, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
71
|
+
body,
|
|
72
|
+
});
|
|
73
|
+
const data = (await response.json());
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const desc = String(data.error_description ?? data.error ?? response.statusText);
|
|
76
|
+
throw new Error(`${options.provider}: 换取 token 失败:${desc}`);
|
|
77
|
+
}
|
|
78
|
+
const refreshToken = String(data.refresh_token ?? "");
|
|
79
|
+
const accessToken = String(data.access_token ?? "");
|
|
80
|
+
if (!refreshToken) {
|
|
81
|
+
throw new Error(`${options.provider}: 响应没有 refresh_token。Google 请确认 access_type=offline&prompt=consent;Microsoft 请确认已请求 offline_access。`);
|
|
82
|
+
}
|
|
83
|
+
if (!accessToken)
|
|
84
|
+
throw new Error(`${options.provider}: 响应缺少 access_token`);
|
|
85
|
+
return {
|
|
86
|
+
accessToken,
|
|
87
|
+
refreshToken,
|
|
88
|
+
expiresIn: Number(data.expires_in ?? 3600),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=oauth-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-auth.js","sourceRoot":"","sources":["../src/oauth-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Short-lived, single-use OAuth authorization sessions keyed by `state`.
|
|
3
|
+
* Kept in memory: a restart simply invalidates pending links.
|
|
4
|
+
*/
|
|
5
|
+
import type { OAuthProviderKind } from "./token-manager.js";
|
|
6
|
+
export declare const OAUTH_SESSION_TTL_MS: number;
|
|
7
|
+
export interface OAuthSession<Requester> {
|
|
8
|
+
readonly provider: OAuthProviderKind;
|
|
9
|
+
readonly state: string;
|
|
10
|
+
readonly codeVerifier: string;
|
|
11
|
+
readonly redirectUri: string;
|
|
12
|
+
readonly requester: Requester;
|
|
13
|
+
readonly expiresAt: number;
|
|
14
|
+
}
|
|
15
|
+
export declare class OAuthSessionRegistry<Requester> {
|
|
16
|
+
#private;
|
|
17
|
+
constructor(options?: {
|
|
18
|
+
now?: () => number;
|
|
19
|
+
ttlMs?: number;
|
|
20
|
+
});
|
|
21
|
+
create(session: Omit<OAuthSession<Requester>, "expiresAt">): OAuthSession<Requester>;
|
|
22
|
+
/** Removes and returns the session; a state can only be consumed once. */
|
|
23
|
+
consume(state: string): OAuthSession<Requester> | undefined;
|
|
24
|
+
pruneExpired(): void;
|
|
25
|
+
get size(): number;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=oauth-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-session.d.ts","sourceRoot":"","sources":["../src/oauth-session.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,eAAO,MAAM,oBAAoB,QAAiB,CAAC;AAEnD,MAAM,WAAW,YAAY,CAAC,SAAS;IACrC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qBAAa,oBAAoB,CAAC,SAAS;;gBAK7B,OAAO,GAAE;QAAE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO;IAKhE,MAAM,CACJ,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,GAClD,YAAY,CAAC,SAAS,CAAC;IAU1B,0EAA0E;IAC1E,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,GAAG,SAAS;IAY3D,YAAY,IAAI,IAAI;IASpB,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|