@ganju/utils 0.0.4 → 0.0.6

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.
@@ -0,0 +1,25 @@
1
+ /** The SHA-256 of a presented token, in the encoding the column stores. */
2
+ export declare const hashAccessToken: (token: string) => Promise<string>;
3
+ /**
4
+ * Cheap enough to run before the database is touched: a bearer token that does
5
+ * not carry the prefix is an OAuth token, and belongs on the other path.
6
+ */
7
+ export declare const isAccessToken: (token: string) => boolean;
8
+ /**
9
+ * What the dashboard shows beside a token's name.
10
+ *
11
+ * Enough to tell two rows apart when someone is deciding which to revoke, and
12
+ * deliberately taken from the *front* of the secret rather than the end: a
13
+ * prefix narrows a brute-force search by exactly as much as a suffix would, and
14
+ * a value people are used to seeing truncated at the end reads as complete when
15
+ * it is the end that is shown.
16
+ */
17
+ export declare const accessTokenHint: (token: string) => string;
18
+ export interface MintedAccessToken {
19
+ /** The only time this value exists. Returned to the caller, never stored. */
20
+ token: string;
21
+ tokenHash: string;
22
+ hint: string;
23
+ }
24
+ export declare const mintAccessToken: () => Promise<MintedAccessToken>;
25
+ //# sourceMappingURL=accessToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessToken.d.ts","sourceRoot":"","sources":["../src/accessToken.ts"],"names":[],"mappings":"AA+BA,2EAA2E;AAC3E,eAAO,MAAM,eAAe,GAAU,OAAO,MAAM,KAAG,OAAO,CAAC,MAAM,CAMnE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,OACG,CAAC;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,KAAG,MAI1B,CAAC;AAEvB,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,eAAe,QAAa,OAAO,CAAC,iBAAiB,CASjE,CAAC"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mintAccessToken = exports.accessTokenHint = exports.isAccessToken = exports.hashAccessToken = void 0;
4
+ const constants_1 = require("./constants");
5
+ /**
6
+ * Minting and recognising a personal access token.
7
+ *
8
+ * The value exists in plaintext exactly once — in the response to the request
9
+ * that created it — and what the database holds is its SHA-256. That is the
10
+ * property the whole credential rests on: a leaked backup, a stray log line, or
11
+ * a support engineer reading the row learns nothing they could present as the
12
+ * token, and there is no path in the product that can print one back, because
13
+ * there is nothing to print.
14
+ *
15
+ * SHA-256 rather than a password hash on purpose. A password is a low-entropy
16
+ * secret a person chose, so the cost of hashing it is what stands between a
17
+ * stolen table and the passwords in it; this is 32 bytes from a CSPRNG, where
18
+ * that cost buys nothing and would be paid on every authenticated request. It
19
+ * is also the hash `oauth_client.client_secret` already uses in this system, so
20
+ * there is one answer here to "how is a machine credential stored".
21
+ */
22
+ const HINT_SEPARATOR = '…';
23
+ const base64url = (bytes) => {
24
+ let binary = '';
25
+ for (const byte of bytes)
26
+ binary += String.fromCharCode(byte);
27
+ return btoa(binary)
28
+ .replace(/\+/g, '-')
29
+ .replace(/\//g, '_')
30
+ .replace(/=+$/, '');
31
+ };
32
+ /** The SHA-256 of a presented token, in the encoding the column stores. */
33
+ const hashAccessToken = async (token) => {
34
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(token));
35
+ return base64url(new Uint8Array(digest));
36
+ };
37
+ exports.hashAccessToken = hashAccessToken;
38
+ /**
39
+ * Cheap enough to run before the database is touched: a bearer token that does
40
+ * not carry the prefix is an OAuth token, and belongs on the other path.
41
+ */
42
+ const isAccessToken = (token) => token.startsWith(constants_1.constants.ACCESS_TOKEN_PREFIX);
43
+ exports.isAccessToken = isAccessToken;
44
+ /**
45
+ * What the dashboard shows beside a token's name.
46
+ *
47
+ * Enough to tell two rows apart when someone is deciding which to revoke, and
48
+ * deliberately taken from the *front* of the secret rather than the end: a
49
+ * prefix narrows a brute-force search by exactly as much as a suffix would, and
50
+ * a value people are used to seeing truncated at the end reads as complete when
51
+ * it is the end that is shown.
52
+ */
53
+ const accessTokenHint = (token) => `${token.slice(0, constants_1.constants.ACCESS_TOKEN_PREFIX.length + constants_1.constants.ACCESS_TOKEN_HINT_CHARS)}${HINT_SEPARATOR}`;
54
+ exports.accessTokenHint = accessTokenHint;
55
+ const mintAccessToken = async () => {
56
+ const bytes = new Uint8Array(constants_1.constants.ACCESS_TOKEN_BYTES);
57
+ crypto.getRandomValues(bytes);
58
+ const token = `${constants_1.constants.ACCESS_TOKEN_PREFIX}${base64url(bytes)}`;
59
+ return {
60
+ token,
61
+ tokenHash: await (0, exports.hashAccessToken)(token),
62
+ hint: (0, exports.accessTokenHint)(token)
63
+ };
64
+ };
65
+ exports.mintAccessToken = mintAccessToken;
@@ -451,6 +451,7 @@ export declare const constants: {
451
451
  CALCOM_API_VERSION_EVENT_TYPES: string;
452
452
  CALCOM_API_VERSION_SLOTS: string;
453
453
  CALCOM_API_VERSION_BOOKINGS: string;
454
+ CALCOM_API_VERSION_ME: string;
454
455
  CALCOM_TOOL_KEY_PREFIX: string;
455
456
  TAVILY_API_BASE: string;
456
457
  TAVILY_SEARCH_DEPTH_BASIC: "basic";
@@ -618,6 +619,15 @@ export declare const constants: {
618
619
  CLI_OAUTH_REDIRECT_PORTS: number[];
619
620
  CLI_OAUTH_SCOPES: string[];
620
621
  CLI_TOKEN_REFRESH_SKEW_SECONDS: number;
622
+ ACCESS_TOKEN_PREFIX: string;
623
+ ACCESS_TOKEN_BYTES: number;
624
+ ACCESS_TOKEN_HINT_CHARS: number;
625
+ ACCESS_TOKEN_NAME_MAX: number;
626
+ ACCESS_TOKEN_MAX_PER_PROJECT: number;
627
+ ACCESS_TOKEN_MAX_EXPIRY_DAYS: number;
628
+ ACCESS_TOKEN_LAST_USED_INTERVAL_MS: number;
629
+ ACCESS_TOKEN_UNSCOPED_PATHS: string[];
630
+ ACCESS_TOKEN_SCOPE_MESSAGE: string;
621
631
  CUSTOM_CODE_LOGS_DEFAULT_LIMIT: number;
622
632
  CUSTOM_CODE_LOGS_MAX_LIMIT: number;
623
633
  BOT_GRANT_TYPE: string;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
package/dist/constants.js CHANGED
@@ -877,6 +877,7 @@ const CALCOM_API_BASE = 'https://api.cal.com/v2';
877
877
  const CALCOM_API_VERSION_EVENT_TYPES = '2024-06-14';
878
878
  const CALCOM_API_VERSION_SLOTS = '2024-09-04';
879
879
  const CALCOM_API_VERSION_BOOKINGS = '2026-02-25';
880
+ const CALCOM_API_VERSION_ME = '2024-06-14';
880
881
  const CALCOM_TOOL_KEY_PREFIX = 'calcom-';
881
882
  // Tavily web search. The key is validated against the live API before it is
882
883
  // persisted (a minimal 1-result search), then stored as an artifact_credential
@@ -1439,6 +1440,46 @@ const ARTIFACT_SCOPE_PREFIX = 'artifact:';
1439
1440
  // loopback matching, where the port is ignored for a 127.0.0.0/8 redirect, but
1440
1441
  // that is one library's behaviour and this is a login that has already opened
1441
1442
  // someone's browser by the time it would fail.
1443
+ // A personal access token — the durable credential a machine with no browser
1444
+ // uses, where an OAuth access token's one hour is not enough. Bound to one
1445
+ // project, because that is the unit a deploy pipeline works on: one repository,
1446
+ // one artifact, one credential in its CI settings.
1447
+ //
1448
+ // The prefix is part of the value rather than decoration: it is what lets the
1449
+ // middleware tell one of these from an OAuth token before it decides which
1450
+ // lookup to make, and it is what secret scanners match on when one leaks into a
1451
+ // repository. The rest is 32 random bytes, base64url — the token is the only
1452
+ // place the value ever exists in plaintext, since what is stored is its hash.
1453
+ const ACCESS_TOKEN_PREFIX = 'ganju_pat_';
1454
+ const ACCESS_TOKEN_BYTES = 32;
1455
+ // Enough of the secret to recognise a row by, and not enough to be worth
1456
+ // stealing. Shown in the dashboard and by `ganju token list` beside the name.
1457
+ const ACCESS_TOKEN_HINT_CHARS = 6;
1458
+ const ACCESS_TOKEN_NAME_MAX = 100;
1459
+ // A ceiling per project, so a compromised session cannot quietly mint an
1460
+ // unbounded set of credentials that each survive the session being ended.
1461
+ const ACCESS_TOKEN_MAX_PER_PROJECT = 20;
1462
+ // An expiry is optional, because a scheduled deploy that dies on a date nobody
1463
+ // wrote down is its own kind of outage — but a year is the longest we will
1464
+ // write one for.
1465
+ const ACCESS_TOKEN_MAX_EXPIRY_DAYS = 365;
1466
+ // `last_used_at` is a convenience, not an audit log, so it is written at most
1467
+ // this often per token rather than on every request. The question it answers —
1468
+ // "is anything still using this, or can I revoke it" — does not get a better
1469
+ // answer from minute-level precision, and the write would otherwise land on the
1470
+ // hot path of every CI request.
1471
+ const ACCESS_TOKEN_LAST_USED_INTERVAL_MS = 5 * 60 * 1000;
1472
+ // The only path a personal access token may reach without naming the project it
1473
+ // is scoped to, and only on GET. `/me` reports who the token is, which is how
1474
+ // the CLI confirms a machine is authenticated at all, and tells it nothing it
1475
+ // does not already hold.
1476
+ //
1477
+ // Everything else is refused, organization routes included — billing, members,
1478
+ // the model configs and the other projects are not what a deploy credential is
1479
+ // for. The list is deliberately this short: a route added later is closed by
1480
+ // omission rather than open by it.
1481
+ const ACCESS_TOKEN_UNSCOPED_PATHS = ['/me'];
1482
+ const ACCESS_TOKEN_SCOPE_MESSAGE = 'This token is scoped to a different project';
1442
1483
  // Recent custom-tool invocations, as `ganju logs` reads them.
1443
1484
  const CUSTOM_CODE_LOGS_DEFAULT_LIMIT = 20;
1444
1485
  const CUSTOM_CODE_LOGS_MAX_LIMIT = 100;
@@ -2100,6 +2141,7 @@ exports.constants = {
2100
2141
  CALCOM_API_VERSION_EVENT_TYPES,
2101
2142
  CALCOM_API_VERSION_SLOTS,
2102
2143
  CALCOM_API_VERSION_BOOKINGS,
2144
+ CALCOM_API_VERSION_ME,
2103
2145
  CALCOM_TOOL_KEY_PREFIX,
2104
2146
  TAVILY_API_BASE,
2105
2147
  TAVILY_SEARCH_DEPTH_BASIC,
@@ -2267,6 +2309,15 @@ exports.constants = {
2267
2309
  CLI_OAUTH_REDIRECT_PORTS: cliConstants_1.CLI_OAUTH_REDIRECT_PORTS,
2268
2310
  CLI_OAUTH_SCOPES: cliConstants_1.CLI_OAUTH_SCOPES,
2269
2311
  CLI_TOKEN_REFRESH_SKEW_SECONDS: cliConstants_1.CLI_TOKEN_REFRESH_SKEW_SECONDS,
2312
+ ACCESS_TOKEN_PREFIX,
2313
+ ACCESS_TOKEN_BYTES,
2314
+ ACCESS_TOKEN_HINT_CHARS,
2315
+ ACCESS_TOKEN_NAME_MAX,
2316
+ ACCESS_TOKEN_MAX_PER_PROJECT,
2317
+ ACCESS_TOKEN_MAX_EXPIRY_DAYS,
2318
+ ACCESS_TOKEN_LAST_USED_INTERVAL_MS,
2319
+ ACCESS_TOKEN_UNSCOPED_PATHS,
2320
+ ACCESS_TOKEN_SCOPE_MESSAGE,
2270
2321
  CUSTOM_CODE_LOGS_DEFAULT_LIMIT,
2271
2322
  CUSTOM_CODE_LOGS_MAX_LIMIT,
2272
2323
  BOT_GRANT_TYPE,
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ import type { OAuthProviderConfig } from './oauthProviders';
15
15
  import type { ExposableResource } from './exposedResource';
16
16
  import type { CustomCodeProject, ProjectPathIssue } from './customCodeProject';
17
17
  import type { AttachmentResource, ResolvedAttachment, ResolveAttachmentResult } from './attachment';
18
+ import type { MintedAccessToken } from './accessToken';
18
19
  import type { CatalogGroup, CatalogTool, CatalogToolDescriptor, ToolGroupKey, ToolKey } from './toolCatalog';
19
20
  import type { Separator, ChunkMetadata, PreparedChunk } from './chunking';
20
21
  import type { RateLimitRetryOptions } from './retry';
@@ -868,6 +869,28 @@ export declare const utils: {
868
869
  userId: import("zod").ZodUUID;
869
870
  organizationId: import("zod").ZodUUID;
870
871
  }, import("zod/v4/core").$strip>;
872
+ ACCESS_TOKEN_CREATE: import("zod").ZodObject<{
873
+ name: import("zod").ZodString;
874
+ expiresInDays: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodNumber>>;
875
+ userId: import("zod").ZodUUID;
876
+ organizationId: import("zod").ZodUUID;
877
+ projectId: import("zod").ZodUUID;
878
+ }, import("zod/v4/core").$strip>;
879
+ ACCESS_TOKEN_CREATE_VIEW: import("zod").ZodObject<{
880
+ name: import("zod").ZodString;
881
+ expiresInDays: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodNumber>>;
882
+ }, import("zod/v4/core").$strip>;
883
+ ACCESS_TOKEN_LIST: import("zod").ZodObject<{
884
+ userId: import("zod").ZodUUID;
885
+ organizationId: import("zod").ZodUUID;
886
+ projectId: import("zod").ZodUUID;
887
+ }, import("zod/v4/core").$strip>;
888
+ ACCESS_TOKEN_REMOVE: import("zod").ZodObject<{
889
+ tokenId: import("zod").ZodUUID;
890
+ userId: import("zod").ZodUUID;
891
+ organizationId: import("zod").ZodUUID;
892
+ projectId: import("zod").ZodUUID;
893
+ }, import("zod/v4/core").$strip>;
871
894
  CHANNEL_CONFIG: import("zod").ZodObject<{
872
895
  debounceMs: import("zod").ZodOptional<import("zod").ZodNumber>;
873
896
  }, import("zod/v4/core").$loose>;
@@ -2138,6 +2161,7 @@ export declare const utils: {
2138
2161
  CALCOM_API_VERSION_EVENT_TYPES: string;
2139
2162
  CALCOM_API_VERSION_SLOTS: string;
2140
2163
  CALCOM_API_VERSION_BOOKINGS: string;
2164
+ CALCOM_API_VERSION_ME: string;
2141
2165
  CALCOM_TOOL_KEY_PREFIX: string;
2142
2166
  TAVILY_API_BASE: string;
2143
2167
  TAVILY_SEARCH_DEPTH_BASIC: "basic";
@@ -2305,6 +2329,15 @@ export declare const utils: {
2305
2329
  CLI_OAUTH_REDIRECT_PORTS: number[];
2306
2330
  CLI_OAUTH_SCOPES: string[];
2307
2331
  CLI_TOKEN_REFRESH_SKEW_SECONDS: number;
2332
+ ACCESS_TOKEN_PREFIX: string;
2333
+ ACCESS_TOKEN_BYTES: number;
2334
+ ACCESS_TOKEN_HINT_CHARS: number;
2335
+ ACCESS_TOKEN_NAME_MAX: number;
2336
+ ACCESS_TOKEN_MAX_PER_PROJECT: number;
2337
+ ACCESS_TOKEN_MAX_EXPIRY_DAYS: number;
2338
+ ACCESS_TOKEN_LAST_USED_INTERVAL_MS: number;
2339
+ ACCESS_TOKEN_UNSCOPED_PATHS: string[];
2340
+ ACCESS_TOKEN_SCOPE_MESSAGE: string;
2308
2341
  CUSTOM_CODE_LOGS_DEFAULT_LIMIT: number;
2309
2342
  CUSTOM_CODE_LOGS_MAX_LIMIT: number;
2310
2343
  BOT_GRANT_TYPE: string;
@@ -2386,6 +2419,10 @@ export declare const utils: {
2386
2419
  languageFromHeader: (header?: string | null) => string;
2387
2420
  slugifyTitle: (title: string) => string;
2388
2421
  resourceUriFromTitle: (title: string) => string;
2422
+ accessTokenHint: (token: string) => string;
2423
+ hashAccessToken: (token: string) => Promise<string>;
2424
+ isAccessToken: (token: string) => boolean;
2425
+ mintAccessToken: () => Promise<MintedAccessToken>;
2389
2426
  TOOL_CATALOG: readonly [{
2390
2427
  readonly key: "gmail";
2391
2428
  readonly title: "Gmail";
@@ -2842,8 +2879,14 @@ export declare const utils: {
2842
2879
  buildReauthMetadata: (previous: Record<string, unknown> | null, reason: string) => Record<string, unknown>;
2843
2880
  clearReauthMetadata: (previous: Record<string, unknown> | null) => Record<string, unknown> | null;
2844
2881
  isCredentialNeedingReauth: (metadata: unknown) => boolean;
2882
+ isValidTimeZone: (value: unknown) => value is string;
2883
+ readCredentialTimeZone: (metadata: unknown) => string | null;
2884
+ credentialTimeZoneIsStale: (metadata: unknown, now?: number) => boolean;
2885
+ writeCredentialTimeZone: (previous: unknown, timeZone: string | null, now?: Date) => Record<string, unknown>;
2886
+ fetchGoogleCalendarTimeZone: (accessToken: string) => Promise<string | null>;
2887
+ fetchCalcomTimeZone: (apiKey: string) => Promise<string | null>;
2845
2888
  PlanLimitError: typeof PlanLimitError;
2846
2889
  isPlanLimitError: (error: unknown) => error is PlanLimitError;
2847
2890
  };
2848
- export type { CustomCodeProject, ProjectPathIssue, CatalogGroup, CatalogTool, CatalogToolDescriptor, ToolGroupKey, ToolKey, CalendarConfigField, JsonSchema, SchemaViolation, MimeAttachment, MimeMessageInput, GmailOperation, GmailSendRequest, GmailSendResponse, OutlookOperation, OutlookSendRequest, OutlookSendResponse, SlackOperation, SlackSendRequest, SlackSendResponse, SlackSendRemoteResourceRequest, TelegramSendRequest, TelegramSendResponse, TelegramSendRemoteResourceRequest, DiscordSendRequest, DiscordSendResponse, DiscordSendRemoteResourceRequest, WhatsappSendRequest, WhatsappSendResponse, WhatsappSendRemoteResourceRequest, EnvSource, Separator, ChunkMetadata, PreparedChunk, ExtractedDocument, ExtractedDocumentMetadata, ExtractedDocumentSource, RateLimitRetryOptions, QueueBatchLike, QueueMessageLike, ProcessQueueBatchHandlers, ChannelNotifier, ToolStatusEvent, BufferedChannelMessage, ChannelBufferEnvelope, ChannelBufferFlush, Source, ResourceUrlContext, SourceButton, RefreshOAuthTokenInput, RefreshedOAuthToken, HttpEndpointToolConfig, McpProxyToolConfig, McpProxyDiscoveredTool, McpProxyDiscoveredResource, McpProxyDiscoveredPrompt, McpProxyDiscovery, CustomCodeToolConfig, CustomCodeToolManifest, CustomCodeManifest, CustomCodeSendFile, CustomCodeCreateResource, CustomCodeTokenPayload, OAuthProviderConfig, AttachmentResource, ResolvedAttachment, ResolveAttachmentResult, ExposableResource, PlanLimitDetails, PlanLimits };
2891
+ export type { MintedAccessToken, CustomCodeProject, ProjectPathIssue, CatalogGroup, CatalogTool, CatalogToolDescriptor, ToolGroupKey, ToolKey, CalendarConfigField, JsonSchema, SchemaViolation, MimeAttachment, MimeMessageInput, GmailOperation, GmailSendRequest, GmailSendResponse, OutlookOperation, OutlookSendRequest, OutlookSendResponse, SlackOperation, SlackSendRequest, SlackSendResponse, SlackSendRemoteResourceRequest, TelegramSendRequest, TelegramSendResponse, TelegramSendRemoteResourceRequest, DiscordSendRequest, DiscordSendResponse, DiscordSendRemoteResourceRequest, WhatsappSendRequest, WhatsappSendResponse, WhatsappSendRemoteResourceRequest, EnvSource, Separator, ChunkMetadata, PreparedChunk, ExtractedDocument, ExtractedDocumentMetadata, ExtractedDocumentSource, RateLimitRetryOptions, QueueBatchLike, QueueMessageLike, ProcessQueueBatchHandlers, ChannelNotifier, ToolStatusEvent, BufferedChannelMessage, ChannelBufferEnvelope, ChannelBufferFlush, Source, ResourceUrlContext, SourceButton, RefreshOAuthTokenInput, RefreshedOAuthToken, HttpEndpointToolConfig, McpProxyToolConfig, McpProxyDiscoveredTool, McpProxyDiscoveredResource, McpProxyDiscoveredPrompt, McpProxyDiscovery, CustomCodeToolConfig, CustomCodeToolManifest, CustomCodeManifest, CustomCodeSendFile, CustomCodeCreateResource, CustomCodeTokenPayload, OAuthProviderConfig, AttachmentResource, ResolvedAttachment, ResolveAttachmentResult, ExposableResource, PlanLimitDetails, PlanLimits };
2849
2892
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aArGf,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;kBAhHiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBA0DF,CAAC;oBAEd,CAAC;;;;aA0BkB,CAAC;mBAA8B,CAAC;WACvC,CAAC;gBACf,CAAC;gBAA4B,CAAA;;;mBAoBK,CAAC;oBACjC,CAAC;;;;;;;;;;;;;;;kBA3Dc,CAAC;;;;;;;sBAqCR,CAAC;;;;;;;;;CA2Of,CAAC;AAEF,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,YAAY,EACZ,OAAO,EACP,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,8BAA8B,EAC9B,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EACjC,kBAAkB,EAClB,mBAAmB,EACnB,gCAAgC,EAChC,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EACjC,SAAS,EACT,SAAS,EACT,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,MAAM,EACN,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACX,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aApHf,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;kBAhHiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBA0DF,CAAC;oBAEd,CAAC;;;;aA0BkB,CAAC;mBAA8B,CAAC;WACvC,CAAC;gBACf,CAAC;gBAA4B,CAAA;;;mBAoBK,CAAC;oBACjC,CAAC;;;;;;;;;;;;;;;kBA3Dc,CAAC;;;;;;;sBAqCR,CAAC;;;;;;;;;;;;;;;CAoQf,CAAC;AAEF,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,YAAY,EACZ,OAAO,EACP,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,8BAA8B,EAC9B,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EACjC,kBAAkB,EAClB,mBAAmB,EACnB,gCAAgC,EAChC,mBAAmB,EACnB,oBAAoB,EACpB,iCAAiC,EACjC,SAAS,EACT,SAAS,EACT,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,MAAM,EACN,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACX,CAAC"}
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ const languageCookieDomain_1 = require("./languageCookieDomain");
31
31
  const localizeZodIssue_1 = require("./localizeZodIssue");
32
32
  const slugifyTitle_1 = require("./slugifyTitle");
33
33
  const resourceUri_1 = require("./resourceUri");
34
+ const accessToken_1 = require("./accessToken");
34
35
  const toolCatalog_1 = require("./toolCatalog");
35
36
  const tallyUsageKinds_1 = require("./tallyUsageKinds");
36
37
  const formatFilename_1 = require("./formatFilename");
@@ -44,6 +45,7 @@ const channelNotifier_1 = require("./channelNotifier");
44
45
  const channelDebounce_1 = require("./channelDebounce");
45
46
  const sources_1 = require("./sources");
46
47
  const oauth_1 = require("./oauth");
48
+ const vendorTimeZone_1 = require("./vendorTimeZone");
47
49
  const planLimitError_1 = require("./planLimitError");
48
50
  exports.utils = {
49
51
  Schema: schema_1.Schema,
@@ -113,6 +115,10 @@ exports.utils = {
113
115
  languageFromHeader: localizeZodIssue_1.languageFromHeader,
114
116
  slugifyTitle: slugifyTitle_1.slugifyTitle,
115
117
  resourceUriFromTitle: resourceUri_1.resourceUriFromTitle,
118
+ accessTokenHint: accessToken_1.accessTokenHint,
119
+ hashAccessToken: accessToken_1.hashAccessToken,
120
+ isAccessToken: accessToken_1.isAccessToken,
121
+ mintAccessToken: accessToken_1.mintAccessToken,
116
122
  TOOL_CATALOG: toolCatalog_1.TOOL_CATALOG,
117
123
  TOOL_KEYS: toolCatalog_1.TOOL_KEYS,
118
124
  describeCatalogTool: toolCatalog_1.describeCatalogTool,
@@ -152,6 +158,12 @@ exports.utils = {
152
158
  buildReauthMetadata: oauth_1.buildReauthMetadata,
153
159
  clearReauthMetadata: oauth_1.clearReauthMetadata,
154
160
  isCredentialNeedingReauth: oauth_1.isCredentialNeedingReauth,
161
+ isValidTimeZone: vendorTimeZone_1.isValidTimeZone,
162
+ readCredentialTimeZone: vendorTimeZone_1.readCredentialTimeZone,
163
+ credentialTimeZoneIsStale: vendorTimeZone_1.credentialTimeZoneIsStale,
164
+ writeCredentialTimeZone: vendorTimeZone_1.writeCredentialTimeZone,
165
+ fetchGoogleCalendarTimeZone: vendorTimeZone_1.fetchGoogleCalendarTimeZone,
166
+ fetchCalcomTimeZone: vendorTimeZone_1.fetchCalcomTimeZone,
155
167
  PlanLimitError: planLimitError_1.PlanLimitError,
156
168
  isPlanLimitError: planLimitError_1.isPlanLimitError
157
169
  };
package/dist/schema.d.ts CHANGED
@@ -1255,6 +1255,28 @@ export declare const Schema: {
1255
1255
  userId: z.ZodUUID;
1256
1256
  organizationId: z.ZodUUID;
1257
1257
  }, z.core.$strip>;
1258
+ ACCESS_TOKEN_CREATE: z.ZodObject<{
1259
+ name: z.ZodString;
1260
+ expiresInDays: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
1261
+ userId: z.ZodUUID;
1262
+ organizationId: z.ZodUUID;
1263
+ projectId: z.ZodUUID;
1264
+ }, z.core.$strip>;
1265
+ ACCESS_TOKEN_CREATE_VIEW: z.ZodObject<{
1266
+ name: z.ZodString;
1267
+ expiresInDays: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
1268
+ }, z.core.$strip>;
1269
+ ACCESS_TOKEN_LIST: z.ZodObject<{
1270
+ userId: z.ZodUUID;
1271
+ organizationId: z.ZodUUID;
1272
+ projectId: z.ZodUUID;
1273
+ }, z.core.$strip>;
1274
+ ACCESS_TOKEN_REMOVE: z.ZodObject<{
1275
+ tokenId: z.ZodUUID;
1276
+ userId: z.ZodUUID;
1277
+ organizationId: z.ZodUUID;
1278
+ projectId: z.ZodUUID;
1279
+ }, z.core.$strip>;
1258
1280
  CHANNEL_CONFIG: z.ZodObject<{
1259
1281
  debounceMs: z.ZodOptional<z.ZodNumber>;
1260
1282
  }, z.core.$loose>;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA+qBxB,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0FrB,CAAC;AAyDN,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;iBAmCpB,CAAC;AAWH,QAAA,MAAM,kBAAkB;;;;;;;;;;iBA+CtB,CAAC;AAiCH,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAapB,CAAC;AAYH,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCxB,CAAC;AA4LH,QAAA,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4DpC,CAAC;AA4BL,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAkDzB,CAAC;AAwGH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsGlB,CAAC;AAGF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAG1E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAIlE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAMtE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAGtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAMtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAIvE,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAC5C,OAAO,kCAAkC,CAC1C,CAAC;AAKF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAKD,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAC1E;AAMD,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,0BAA0B,EAAE,CAAC;IACzC,OAAO,CAAC,EAAE,wBAAwB,EAAE,CAAC;CACtC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA6tBxB,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0FrB,CAAC;AAyDN,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;iBAmCpB,CAAC;AAWH,QAAA,MAAM,kBAAkB;;;;;;;;;;iBA+CtB,CAAC;AAiCH,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAapB,CAAC;AAYH,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCxB,CAAC;AA4LH,QAAA,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4DpC,CAAC;AA4BL,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAkDzB,CAAC;AAwGH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0GlB,CAAC;AAGF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAG1E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAIlE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAMtE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAGtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAMtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAIvE,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAC5C,OAAO,kCAAkC,CAC1C,CAAC;AAKF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAKD,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAC1E;AAMD,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,0BAA0B,EAAE,CAAC;IACzC,OAAO,CAAC,EAAE,wBAAwB,EAAE,CAAC;CACtC"}
package/dist/schema.js CHANGED
@@ -519,6 +519,48 @@ const ORGANIZATION_REMOVE_LLM = zod_1.z.object({
519
519
  userId: zod_1.z.uuid(),
520
520
  organizationId: zod_1.z.uuid()
521
521
  });
522
+ // Personal access tokens. `name` is required rather than optional because it is
523
+ // the only thing anyone has to go on when deciding whether revoking a row will
524
+ // break a deploy, and a list of unnamed credentials is a list nobody prunes.
525
+ //
526
+ // `expiresInDays` is what the caller sends; `expiresAt` is what the row stores.
527
+ // A duration is what someone actually decides ("ninety days"), and computing the
528
+ // date on the server means a clock the client got wrong cannot mint a token that
529
+ // outlives what was asked for. Null is a token that does not expire, which is a
530
+ // real answer for a scheduled deploy and is why it is spelled explicitly rather
531
+ // than reached by leaving the field out.
532
+ const ACCESS_TOKEN_CREATE = zod_1.z.object({
533
+ name: zod_1.z.string().trim().min(1).max(constants_1.constants.ACCESS_TOKEN_NAME_MAX),
534
+ expiresInDays: zod_1.z
535
+ .number()
536
+ .int()
537
+ .min(1)
538
+ .max(constants_1.constants.ACCESS_TOKEN_MAX_EXPIRY_DAYS)
539
+ .nullable()
540
+ .optional(),
541
+ userId: zod_1.z.uuid(),
542
+ organizationId: zod_1.z.uuid(),
543
+ projectId: zod_1.z.uuid()
544
+ });
545
+ const ACCESS_TOKEN_LIST = zod_1.z.object({
546
+ userId: zod_1.z.uuid(),
547
+ organizationId: zod_1.z.uuid(),
548
+ projectId: zod_1.z.uuid()
549
+ });
550
+ const ACCESS_TOKEN_REMOVE = zod_1.z.object({
551
+ tokenId: zod_1.z.uuid(),
552
+ userId: zod_1.z.uuid(),
553
+ organizationId: zod_1.z.uuid(),
554
+ projectId: zod_1.z.uuid()
555
+ });
556
+ // The ids the route already carries come off: the dashboard reads them from the
557
+ // URL it is on, so a form asking for them again is a second place for them to be
558
+ // wrong.
559
+ const ACCESS_TOKEN_CREATE_VIEW = ACCESS_TOKEN_CREATE.omit({
560
+ userId: true,
561
+ organizationId: true,
562
+ projectId: true
563
+ });
522
564
  const ORGANIZATION_CREATE_LLM_VIEW = ORGANIZATION_CREATE_LLM.omit({
523
565
  userId: true,
524
566
  organizationId: true
@@ -1345,6 +1387,10 @@ exports.Schema = {
1345
1387
  ORGANIZATION_UPDATE_LLM,
1346
1388
  ORGANIZATION_UPDATE_LLM_VIEW,
1347
1389
  ORGANIZATION_REMOVE_LLM,
1390
+ ACCESS_TOKEN_CREATE,
1391
+ ACCESS_TOKEN_CREATE_VIEW,
1392
+ ACCESS_TOKEN_LIST,
1393
+ ACCESS_TOKEN_REMOVE,
1348
1394
  CHANNEL_CONFIG,
1349
1395
  CHANNEL_CREATE,
1350
1396
  CHANNEL_CREATE_VIEW,
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Is this a time zone the runtime actually knows?
3
+ *
4
+ * Everything downstream — `Intl.DateTimeFormat`, Google's `start.timeZone`,
5
+ * Cal.com's `attendee.timeZone` — throws or 400s on a name it cannot resolve.
6
+ * A vendor returning something unexpected must degrade to "we don't know"
7
+ * rather than poison every later call with a value that cannot be used.
8
+ */
9
+ export declare const isValidTimeZone: (value: unknown) => value is string;
10
+ /** The cached zone on a credential's metadata, or null. */
11
+ export declare const readCredentialTimeZone: (metadata: unknown) => string | null;
12
+ /**
13
+ * Should we ask the vendor again?
14
+ *
15
+ * True when there is nothing cached, when the stamp is missing or unreadable,
16
+ * or when the TTL has passed. A cache with no stamp is treated as stale rather
17
+ * than as fresh-forever — the conservative direction, since the only cost is
18
+ * one request.
19
+ */
20
+ export declare const credentialTimeZoneIsStale: (metadata: unknown, now?: number) => boolean;
21
+ /**
22
+ * Merge a freshly read zone into a credential's metadata.
23
+ *
24
+ * Merges rather than replaces, because this column also carries the reauth
25
+ * markers — writing a bare `{ timeZone }` here would clear `needsReauth` and
26
+ * silently re-enable a connection the refresh path had flagged as broken.
27
+ *
28
+ * A null zone (the vendor could not tell us) still stamps the check, so a
29
+ * provider that never reports one is asked once a day rather than on every
30
+ * single call.
31
+ */
32
+ export declare const writeCredentialTimeZone: (previous: unknown, timeZone: string | null, now?: Date) => Record<string, unknown>;
33
+ /**
34
+ * The zone Google Calendar is configured in, from the primary calendar.
35
+ *
36
+ * Deliberately NOT `GET /users/me/settings/timezone`, which is the more
37
+ * direct answer to "what did the user configure" and needs
38
+ * `calendar.settings.readonly` — a scope we do not request and could not add
39
+ * without sending every already-connected user back through consent. The
40
+ * primary calendar's zone is the same value in every case that matters, and
41
+ * `calendar.readonly` already covers it.
42
+ *
43
+ * Returns null on any failure. Not knowing the zone is a state the callers
44
+ * handle; a throw here would take a tool call or a chat turn with it.
45
+ */
46
+ export declare const fetchGoogleCalendarTimeZone: (accessToken: string) => Promise<string | null>;
47
+ /**
48
+ * The zone on the connected Cal.com profile.
49
+ *
50
+ * This is the host's zone — the one their availability is written in — which
51
+ * is what "9am" means when the artifact owner or their bot says it. It is not
52
+ * the attendee's zone; see the booking handler for why we use it there anyway.
53
+ *
54
+ * Same null-on-failure contract as the Google reader above.
55
+ */
56
+ export declare const fetchCalcomTimeZone: (apiKey: string) => Promise<string | null>;
57
+ //# sourceMappingURL=vendorTimeZone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vendorTimeZone.d.ts","sourceRoot":"","sources":["../src/vendorTimeZone.ts"],"names":[],"mappings":"AAiCA;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAQzD,CAAC;AAEF,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,GAAI,UAAU,OAAO,KAAG,MAAM,GAAG,IAInE,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,GACpC,UAAU,OAAO,EACjB,MAAK,MAAmB,KACvB,OAOF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAU,OAAO,EACjB,UAAU,MAAM,GAAG,IAAI,EACvB,MAAK,IAAiB,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAYxB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,2BAA2B,GACtC,aAAa,MAAM,KAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAiBvB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,MAAM,KACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAkBvB,CAAC"}
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ // The time zone the user configured with the vendor, and where we keep it.
3
+ //
4
+ // Every scheduling question the model answers is a time-zone question first:
5
+ // "tomorrow at 9" is not an instant until you know whose 9 it is. We had two
6
+ // sources for that and neither worked. `artifact_tool.config.defaultTimeZone`
7
+ // is only written when the owner opens a dropdown and changes it, so in
8
+ // practice it is empty. The fallbacks underneath it were `undefined` (Google
9
+ // then uses the calendar's own zone, which only helps when the timestamp has no
10
+ // offset) and the string 'UTC' (Cal.com, which books the attendee in UTC and
11
+ // tells nobody).
12
+ //
13
+ // But the user already answered this question — in Google Calendar's settings,
14
+ // and in their Cal.com profile. So we ask the vendor instead of asking the
15
+ // owner again, and the answer becomes the default under any explicit choice.
16
+ //
17
+ // It is cached on `artifact_credential.metadata` because that is what it is a
18
+ // property of: the connected account, not the tool row. One artifact can have
19
+ // six calendar tools installed and they all share one connection, so the
20
+ // connection is the only place the answer belongs exactly once.
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.fetchCalcomTimeZone = exports.fetchGoogleCalendarTimeZone = exports.writeCredentialTimeZone = exports.credentialTimeZoneIsStale = exports.readCredentialTimeZone = exports.isValidTimeZone = void 0;
23
+ const constants_1 = require("./constants");
24
+ // Metadata keys on artifact_credential. Namespaced with a prefix that will not
25
+ // collide with the reauth markers written by the OAuth refresh path.
26
+ const TIME_ZONE_KEY = 'timeZone';
27
+ const TIME_ZONE_CHECKED_AT_KEY = 'timeZoneCheckedAt';
28
+ // How long a cached zone is trusted. A day, because this changes when somebody
29
+ // moves or travels — rarely, and never urgently. The cost of being briefly
30
+ // stale is one meeting in the old zone; the cost of a shorter TTL is a vendor
31
+ // round trip on the path of a tool call.
32
+ const TIME_ZONE_TTL_MS = 24 * 60 * 60 * 1000;
33
+ /**
34
+ * Is this a time zone the runtime actually knows?
35
+ *
36
+ * Everything downstream — `Intl.DateTimeFormat`, Google's `start.timeZone`,
37
+ * Cal.com's `attendee.timeZone` — throws or 400s on a name it cannot resolve.
38
+ * A vendor returning something unexpected must degrade to "we don't know"
39
+ * rather than poison every later call with a value that cannot be used.
40
+ */
41
+ const isValidTimeZone = (value) => {
42
+ if (typeof value !== 'string' || !value.trim())
43
+ return false;
44
+ try {
45
+ new Intl.DateTimeFormat('en-US', { timeZone: value.trim() });
46
+ return true;
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ };
52
+ exports.isValidTimeZone = isValidTimeZone;
53
+ /** The cached zone on a credential's metadata, or null. */
54
+ const readCredentialTimeZone = (metadata) => {
55
+ if (!metadata || typeof metadata !== 'object')
56
+ return null;
57
+ const value = metadata[TIME_ZONE_KEY];
58
+ return (0, exports.isValidTimeZone)(value) ? value.trim() : null;
59
+ };
60
+ exports.readCredentialTimeZone = readCredentialTimeZone;
61
+ /**
62
+ * Should we ask the vendor again?
63
+ *
64
+ * True when there is nothing cached, when the stamp is missing or unreadable,
65
+ * or when the TTL has passed. A cache with no stamp is treated as stale rather
66
+ * than as fresh-forever — the conservative direction, since the only cost is
67
+ * one request.
68
+ */
69
+ const credentialTimeZoneIsStale = (metadata, now = Date.now()) => {
70
+ if (!(0, exports.readCredentialTimeZone)(metadata))
71
+ return true;
72
+ const raw = metadata[TIME_ZONE_CHECKED_AT_KEY];
73
+ if (typeof raw !== 'string')
74
+ return true;
75
+ const checkedAt = Date.parse(raw);
76
+ if (!Number.isFinite(checkedAt))
77
+ return true;
78
+ return now - checkedAt >= TIME_ZONE_TTL_MS;
79
+ };
80
+ exports.credentialTimeZoneIsStale = credentialTimeZoneIsStale;
81
+ /**
82
+ * Merge a freshly read zone into a credential's metadata.
83
+ *
84
+ * Merges rather than replaces, because this column also carries the reauth
85
+ * markers — writing a bare `{ timeZone }` here would clear `needsReauth` and
86
+ * silently re-enable a connection the refresh path had flagged as broken.
87
+ *
88
+ * A null zone (the vendor could not tell us) still stamps the check, so a
89
+ * provider that never reports one is asked once a day rather than on every
90
+ * single call.
91
+ */
92
+ const writeCredentialTimeZone = (previous, timeZone, now = new Date()) => {
93
+ const base = previous && typeof previous === 'object'
94
+ ? { ...previous }
95
+ : {};
96
+ if ((0, exports.isValidTimeZone)(timeZone)) {
97
+ base[TIME_ZONE_KEY] = timeZone.trim();
98
+ }
99
+ else {
100
+ delete base[TIME_ZONE_KEY];
101
+ }
102
+ base[TIME_ZONE_CHECKED_AT_KEY] = now.toISOString();
103
+ return base;
104
+ };
105
+ exports.writeCredentialTimeZone = writeCredentialTimeZone;
106
+ /**
107
+ * The zone Google Calendar is configured in, from the primary calendar.
108
+ *
109
+ * Deliberately NOT `GET /users/me/settings/timezone`, which is the more
110
+ * direct answer to "what did the user configure" and needs
111
+ * `calendar.settings.readonly` — a scope we do not request and could not add
112
+ * without sending every already-connected user back through consent. The
113
+ * primary calendar's zone is the same value in every case that matters, and
114
+ * `calendar.readonly` already covers it.
115
+ *
116
+ * Returns null on any failure. Not knowing the zone is a state the callers
117
+ * handle; a throw here would take a tool call or a chat turn with it.
118
+ */
119
+ const fetchGoogleCalendarTimeZone = async (accessToken) => {
120
+ try {
121
+ const response = await fetch(`${constants_1.constants.GOOGLE_CALENDAR_API_BASE}/calendars/primary`, {
122
+ headers: {
123
+ Authorization: `Bearer ${accessToken}`,
124
+ Accept: 'application/json'
125
+ }
126
+ });
127
+ if (!response.ok)
128
+ return null;
129
+ const payload = (await response.json());
130
+ return (0, exports.isValidTimeZone)(payload?.timeZone) ? payload.timeZone.trim() : null;
131
+ }
132
+ catch {
133
+ return null;
134
+ }
135
+ };
136
+ exports.fetchGoogleCalendarTimeZone = fetchGoogleCalendarTimeZone;
137
+ /**
138
+ * The zone on the connected Cal.com profile.
139
+ *
140
+ * This is the host's zone — the one their availability is written in — which
141
+ * is what "9am" means when the artifact owner or their bot says it. It is not
142
+ * the attendee's zone; see the booking handler for why we use it there anyway.
143
+ *
144
+ * Same null-on-failure contract as the Google reader above.
145
+ */
146
+ const fetchCalcomTimeZone = async (apiKey) => {
147
+ try {
148
+ const response = await fetch(`${constants_1.constants.CALCOM_API_BASE}/me`, {
149
+ headers: {
150
+ Authorization: `Bearer ${apiKey}`,
151
+ 'cal-api-version': constants_1.constants.CALCOM_API_VERSION_ME,
152
+ Accept: 'application/json'
153
+ }
154
+ });
155
+ if (!response.ok)
156
+ return null;
157
+ const payload = (await response.json());
158
+ const value = payload?.data?.timeZone;
159
+ return (0, exports.isValidTimeZone)(value) ? value.trim() : null;
160
+ }
161
+ catch {
162
+ return null;
163
+ }
164
+ };
165
+ exports.fetchCalcomTimeZone = fetchCalcomTimeZone;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ganju/utils",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Shared constants, schemas and helpers for Ganju",
5
5
  "license": "Apache-2.0",
6
6
  "author": "MontoyaAndres <andresmontoyafcb@gmail.com>",
@@ -0,0 +1,79 @@
1
+ import { constants } from './constants';
2
+
3
+ /**
4
+ * Minting and recognising a personal access token.
5
+ *
6
+ * The value exists in plaintext exactly once — in the response to the request
7
+ * that created it — and what the database holds is its SHA-256. That is the
8
+ * property the whole credential rests on: a leaked backup, a stray log line, or
9
+ * a support engineer reading the row learns nothing they could present as the
10
+ * token, and there is no path in the product that can print one back, because
11
+ * there is nothing to print.
12
+ *
13
+ * SHA-256 rather than a password hash on purpose. A password is a low-entropy
14
+ * secret a person chose, so the cost of hashing it is what stands between a
15
+ * stolen table and the passwords in it; this is 32 bytes from a CSPRNG, where
16
+ * that cost buys nothing and would be paid on every authenticated request. It
17
+ * is also the hash `oauth_client.client_secret` already uses in this system, so
18
+ * there is one answer here to "how is a machine credential stored".
19
+ */
20
+
21
+ const HINT_SEPARATOR = '…';
22
+
23
+ const base64url = (bytes: Uint8Array): string => {
24
+ let binary = '';
25
+ for (const byte of bytes) binary += String.fromCharCode(byte);
26
+ return btoa(binary)
27
+ .replace(/\+/g, '-')
28
+ .replace(/\//g, '_')
29
+ .replace(/=+$/, '');
30
+ };
31
+
32
+ /** The SHA-256 of a presented token, in the encoding the column stores. */
33
+ export const hashAccessToken = async (token: string): Promise<string> => {
34
+ const digest = await crypto.subtle.digest(
35
+ 'SHA-256',
36
+ new TextEncoder().encode(token)
37
+ );
38
+ return base64url(new Uint8Array(digest));
39
+ };
40
+
41
+ /**
42
+ * Cheap enough to run before the database is touched: a bearer token that does
43
+ * not carry the prefix is an OAuth token, and belongs on the other path.
44
+ */
45
+ export const isAccessToken = (token: string): boolean =>
46
+ token.startsWith(constants.ACCESS_TOKEN_PREFIX);
47
+
48
+ /**
49
+ * What the dashboard shows beside a token's name.
50
+ *
51
+ * Enough to tell two rows apart when someone is deciding which to revoke, and
52
+ * deliberately taken from the *front* of the secret rather than the end: a
53
+ * prefix narrows a brute-force search by exactly as much as a suffix would, and
54
+ * a value people are used to seeing truncated at the end reads as complete when
55
+ * it is the end that is shown.
56
+ */
57
+ export const accessTokenHint = (token: string): string =>
58
+ `${token.slice(
59
+ 0,
60
+ constants.ACCESS_TOKEN_PREFIX.length + constants.ACCESS_TOKEN_HINT_CHARS
61
+ )}${HINT_SEPARATOR}`;
62
+
63
+ export interface MintedAccessToken {
64
+ /** The only time this value exists. Returned to the caller, never stored. */
65
+ token: string;
66
+ tokenHash: string;
67
+ hint: string;
68
+ }
69
+
70
+ export const mintAccessToken = async (): Promise<MintedAccessToken> => {
71
+ const bytes = new Uint8Array(constants.ACCESS_TOKEN_BYTES);
72
+ crypto.getRandomValues(bytes);
73
+ const token = `${constants.ACCESS_TOKEN_PREFIX}${base64url(bytes)}`;
74
+ return {
75
+ token,
76
+ tokenHash: await hashAccessToken(token),
77
+ hint: accessTokenHint(token)
78
+ };
79
+ };
package/src/constants.ts CHANGED
@@ -1054,6 +1054,7 @@ const CALCOM_API_BASE = 'https://api.cal.com/v2';
1054
1054
  const CALCOM_API_VERSION_EVENT_TYPES = '2024-06-14';
1055
1055
  const CALCOM_API_VERSION_SLOTS = '2024-09-04';
1056
1056
  const CALCOM_API_VERSION_BOOKINGS = '2026-02-25';
1057
+ const CALCOM_API_VERSION_ME = '2024-06-14';
1057
1058
 
1058
1059
  const CALCOM_TOOL_KEY_PREFIX = 'calcom-';
1059
1060
 
@@ -1695,6 +1696,55 @@ const ARTIFACT_SCOPE_PREFIX = 'artifact:';
1695
1696
  // that is one library's behaviour and this is a login that has already opened
1696
1697
  // someone's browser by the time it would fail.
1697
1698
 
1699
+ // A personal access token — the durable credential a machine with no browser
1700
+ // uses, where an OAuth access token's one hour is not enough. Bound to one
1701
+ // project, because that is the unit a deploy pipeline works on: one repository,
1702
+ // one artifact, one credential in its CI settings.
1703
+ //
1704
+ // The prefix is part of the value rather than decoration: it is what lets the
1705
+ // middleware tell one of these from an OAuth token before it decides which
1706
+ // lookup to make, and it is what secret scanners match on when one leaks into a
1707
+ // repository. The rest is 32 random bytes, base64url — the token is the only
1708
+ // place the value ever exists in plaintext, since what is stored is its hash.
1709
+ const ACCESS_TOKEN_PREFIX = 'ganju_pat_';
1710
+ const ACCESS_TOKEN_BYTES = 32;
1711
+
1712
+ // Enough of the secret to recognise a row by, and not enough to be worth
1713
+ // stealing. Shown in the dashboard and by `ganju token list` beside the name.
1714
+ const ACCESS_TOKEN_HINT_CHARS = 6;
1715
+
1716
+ const ACCESS_TOKEN_NAME_MAX = 100;
1717
+
1718
+ // A ceiling per project, so a compromised session cannot quietly mint an
1719
+ // unbounded set of credentials that each survive the session being ended.
1720
+ const ACCESS_TOKEN_MAX_PER_PROJECT = 20;
1721
+
1722
+ // An expiry is optional, because a scheduled deploy that dies on a date nobody
1723
+ // wrote down is its own kind of outage — but a year is the longest we will
1724
+ // write one for.
1725
+ const ACCESS_TOKEN_MAX_EXPIRY_DAYS = 365;
1726
+
1727
+ // `last_used_at` is a convenience, not an audit log, so it is written at most
1728
+ // this often per token rather than on every request. The question it answers —
1729
+ // "is anything still using this, or can I revoke it" — does not get a better
1730
+ // answer from minute-level precision, and the write would otherwise land on the
1731
+ // hot path of every CI request.
1732
+ const ACCESS_TOKEN_LAST_USED_INTERVAL_MS = 5 * 60 * 1000;
1733
+
1734
+ // The only path a personal access token may reach without naming the project it
1735
+ // is scoped to, and only on GET. `/me` reports who the token is, which is how
1736
+ // the CLI confirms a machine is authenticated at all, and tells it nothing it
1737
+ // does not already hold.
1738
+ //
1739
+ // Everything else is refused, organization routes included — billing, members,
1740
+ // the model configs and the other projects are not what a deploy credential is
1741
+ // for. The list is deliberately this short: a route added later is closed by
1742
+ // omission rather than open by it.
1743
+ const ACCESS_TOKEN_UNSCOPED_PATHS = ['/me'];
1744
+
1745
+ const ACCESS_TOKEN_SCOPE_MESSAGE =
1746
+ 'This token is scoped to a different project';
1747
+
1698
1748
  // Recent custom-tool invocations, as `ganju logs` reads them.
1699
1749
  const CUSTOM_CODE_LOGS_DEFAULT_LIMIT = 20;
1700
1750
  const CUSTOM_CODE_LOGS_MAX_LIMIT = 100;
@@ -2429,6 +2479,7 @@ export const constants = {
2429
2479
  CALCOM_API_VERSION_EVENT_TYPES,
2430
2480
  CALCOM_API_VERSION_SLOTS,
2431
2481
  CALCOM_API_VERSION_BOOKINGS,
2482
+ CALCOM_API_VERSION_ME,
2432
2483
  CALCOM_TOOL_KEY_PREFIX,
2433
2484
  TAVILY_API_BASE,
2434
2485
  TAVILY_SEARCH_DEPTH_BASIC,
@@ -2596,6 +2647,15 @@ export const constants = {
2596
2647
  CLI_OAUTH_REDIRECT_PORTS,
2597
2648
  CLI_OAUTH_SCOPES,
2598
2649
  CLI_TOKEN_REFRESH_SKEW_SECONDS,
2650
+ ACCESS_TOKEN_PREFIX,
2651
+ ACCESS_TOKEN_BYTES,
2652
+ ACCESS_TOKEN_HINT_CHARS,
2653
+ ACCESS_TOKEN_NAME_MAX,
2654
+ ACCESS_TOKEN_MAX_PER_PROJECT,
2655
+ ACCESS_TOKEN_MAX_EXPIRY_DAYS,
2656
+ ACCESS_TOKEN_LAST_USED_INTERVAL_MS,
2657
+ ACCESS_TOKEN_UNSCOPED_PATHS,
2658
+ ACCESS_TOKEN_SCOPE_MESSAGE,
2599
2659
  CUSTOM_CODE_LOGS_DEFAULT_LIMIT,
2600
2660
  CUSTOM_CODE_LOGS_MAX_LIMIT,
2601
2661
  BOT_GRANT_TYPE,
package/src/index.ts CHANGED
@@ -135,6 +135,13 @@ import { languageCookieDomain } from './languageCookieDomain';
135
135
  import { localizeZodIssue, languageFromHeader } from './localizeZodIssue';
136
136
  import { slugifyTitle } from './slugifyTitle';
137
137
  import { resourceUriFromTitle } from './resourceUri';
138
+ import {
139
+ accessTokenHint,
140
+ hashAccessToken,
141
+ isAccessToken,
142
+ mintAccessToken
143
+ } from './accessToken';
144
+ import type { MintedAccessToken } from './accessToken';
138
145
  import {
139
146
  TOOL_CATALOG,
140
147
  TOOL_KEYS,
@@ -211,6 +218,14 @@ import {
211
218
  isCredentialNeedingReauth
212
219
  } from './oauth';
213
220
  import type { RefreshOAuthTokenInput, RefreshedOAuthToken } from './oauth';
221
+ import {
222
+ isValidTimeZone,
223
+ readCredentialTimeZone,
224
+ credentialTimeZoneIsStale,
225
+ writeCredentialTimeZone,
226
+ fetchGoogleCalendarTimeZone,
227
+ fetchCalcomTimeZone
228
+ } from './vendorTimeZone';
214
229
  import { PlanLimitError, isPlanLimitError } from './planLimitError';
215
230
  import type { PlanLimitDetails } from './planLimitError';
216
231
  import type { PlanLimits } from './constants';
@@ -283,6 +298,10 @@ export const utils = {
283
298
  languageFromHeader,
284
299
  slugifyTitle,
285
300
  resourceUriFromTitle,
301
+ accessTokenHint,
302
+ hashAccessToken,
303
+ isAccessToken,
304
+ mintAccessToken,
286
305
  TOOL_CATALOG,
287
306
  TOOL_KEYS,
288
307
  describeCatalogTool,
@@ -322,11 +341,18 @@ export const utils = {
322
341
  buildReauthMetadata,
323
342
  clearReauthMetadata,
324
343
  isCredentialNeedingReauth,
344
+ isValidTimeZone,
345
+ readCredentialTimeZone,
346
+ credentialTimeZoneIsStale,
347
+ writeCredentialTimeZone,
348
+ fetchGoogleCalendarTimeZone,
349
+ fetchCalcomTimeZone,
325
350
  PlanLimitError,
326
351
  isPlanLimitError
327
352
  };
328
353
 
329
354
  export type {
355
+ MintedAccessToken,
330
356
  CustomCodeProject,
331
357
  ProjectPathIssue,
332
358
  CatalogGroup,
package/src/schema.ts CHANGED
@@ -603,6 +603,52 @@ const ORGANIZATION_REMOVE_LLM = z.object({
603
603
  organizationId: z.uuid()
604
604
  });
605
605
 
606
+ // Personal access tokens. `name` is required rather than optional because it is
607
+ // the only thing anyone has to go on when deciding whether revoking a row will
608
+ // break a deploy, and a list of unnamed credentials is a list nobody prunes.
609
+ //
610
+ // `expiresInDays` is what the caller sends; `expiresAt` is what the row stores.
611
+ // A duration is what someone actually decides ("ninety days"), and computing the
612
+ // date on the server means a clock the client got wrong cannot mint a token that
613
+ // outlives what was asked for. Null is a token that does not expire, which is a
614
+ // real answer for a scheduled deploy and is why it is spelled explicitly rather
615
+ // than reached by leaving the field out.
616
+ const ACCESS_TOKEN_CREATE = z.object({
617
+ name: z.string().trim().min(1).max(constants.ACCESS_TOKEN_NAME_MAX),
618
+ expiresInDays: z
619
+ .number()
620
+ .int()
621
+ .min(1)
622
+ .max(constants.ACCESS_TOKEN_MAX_EXPIRY_DAYS)
623
+ .nullable()
624
+ .optional(),
625
+ userId: z.uuid(),
626
+ organizationId: z.uuid(),
627
+ projectId: z.uuid()
628
+ });
629
+
630
+ const ACCESS_TOKEN_LIST = z.object({
631
+ userId: z.uuid(),
632
+ organizationId: z.uuid(),
633
+ projectId: z.uuid()
634
+ });
635
+
636
+ const ACCESS_TOKEN_REMOVE = z.object({
637
+ tokenId: z.uuid(),
638
+ userId: z.uuid(),
639
+ organizationId: z.uuid(),
640
+ projectId: z.uuid()
641
+ });
642
+
643
+ // The ids the route already carries come off: the dashboard reads them from the
644
+ // URL it is on, so a form asking for them again is a second place for them to be
645
+ // wrong.
646
+ const ACCESS_TOKEN_CREATE_VIEW = ACCESS_TOKEN_CREATE.omit({
647
+ userId: true,
648
+ organizationId: true,
649
+ projectId: true
650
+ });
651
+
606
652
  const ORGANIZATION_CREATE_LLM_VIEW = ORGANIZATION_CREATE_LLM.omit({
607
653
  userId: true,
608
654
  organizationId: true
@@ -1513,6 +1559,10 @@ export const Schema = {
1513
1559
  ORGANIZATION_UPDATE_LLM,
1514
1560
  ORGANIZATION_UPDATE_LLM_VIEW,
1515
1561
  ORGANIZATION_REMOVE_LLM,
1562
+ ACCESS_TOKEN_CREATE,
1563
+ ACCESS_TOKEN_CREATE_VIEW,
1564
+ ACCESS_TOKEN_LIST,
1565
+ ACCESS_TOKEN_REMOVE,
1516
1566
  CHANNEL_CONFIG,
1517
1567
  CHANNEL_CREATE,
1518
1568
  CHANNEL_CREATE_VIEW,
@@ -0,0 +1,171 @@
1
+ // The time zone the user configured with the vendor, and where we keep it.
2
+ //
3
+ // Every scheduling question the model answers is a time-zone question first:
4
+ // "tomorrow at 9" is not an instant until you know whose 9 it is. We had two
5
+ // sources for that and neither worked. `artifact_tool.config.defaultTimeZone`
6
+ // is only written when the owner opens a dropdown and changes it, so in
7
+ // practice it is empty. The fallbacks underneath it were `undefined` (Google
8
+ // then uses the calendar's own zone, which only helps when the timestamp has no
9
+ // offset) and the string 'UTC' (Cal.com, which books the attendee in UTC and
10
+ // tells nobody).
11
+ //
12
+ // But the user already answered this question — in Google Calendar's settings,
13
+ // and in their Cal.com profile. So we ask the vendor instead of asking the
14
+ // owner again, and the answer becomes the default under any explicit choice.
15
+ //
16
+ // It is cached on `artifact_credential.metadata` because that is what it is a
17
+ // property of: the connected account, not the tool row. One artifact can have
18
+ // six calendar tools installed and they all share one connection, so the
19
+ // connection is the only place the answer belongs exactly once.
20
+
21
+ import { constants } from './constants';
22
+
23
+ // Metadata keys on artifact_credential. Namespaced with a prefix that will not
24
+ // collide with the reauth markers written by the OAuth refresh path.
25
+ const TIME_ZONE_KEY = 'timeZone';
26
+ const TIME_ZONE_CHECKED_AT_KEY = 'timeZoneCheckedAt';
27
+
28
+ // How long a cached zone is trusted. A day, because this changes when somebody
29
+ // moves or travels — rarely, and never urgently. The cost of being briefly
30
+ // stale is one meeting in the old zone; the cost of a shorter TTL is a vendor
31
+ // round trip on the path of a tool call.
32
+ const TIME_ZONE_TTL_MS = 24 * 60 * 60 * 1000;
33
+
34
+ /**
35
+ * Is this a time zone the runtime actually knows?
36
+ *
37
+ * Everything downstream — `Intl.DateTimeFormat`, Google's `start.timeZone`,
38
+ * Cal.com's `attendee.timeZone` — throws or 400s on a name it cannot resolve.
39
+ * A vendor returning something unexpected must degrade to "we don't know"
40
+ * rather than poison every later call with a value that cannot be used.
41
+ */
42
+ export const isValidTimeZone = (value: unknown): value is string => {
43
+ if (typeof value !== 'string' || !value.trim()) return false;
44
+ try {
45
+ new Intl.DateTimeFormat('en-US', { timeZone: value.trim() });
46
+ return true;
47
+ } catch {
48
+ return false;
49
+ }
50
+ };
51
+
52
+ /** The cached zone on a credential's metadata, or null. */
53
+ export const readCredentialTimeZone = (metadata: unknown): string | null => {
54
+ if (!metadata || typeof metadata !== 'object') return null;
55
+ const value = (metadata as Record<string, unknown>)[TIME_ZONE_KEY];
56
+ return isValidTimeZone(value) ? value.trim() : null;
57
+ };
58
+
59
+ /**
60
+ * Should we ask the vendor again?
61
+ *
62
+ * True when there is nothing cached, when the stamp is missing or unreadable,
63
+ * or when the TTL has passed. A cache with no stamp is treated as stale rather
64
+ * than as fresh-forever — the conservative direction, since the only cost is
65
+ * one request.
66
+ */
67
+ export const credentialTimeZoneIsStale = (
68
+ metadata: unknown,
69
+ now: number = Date.now()
70
+ ): boolean => {
71
+ if (!readCredentialTimeZone(metadata)) return true;
72
+ const raw = (metadata as Record<string, unknown>)[TIME_ZONE_CHECKED_AT_KEY];
73
+ if (typeof raw !== 'string') return true;
74
+ const checkedAt = Date.parse(raw);
75
+ if (!Number.isFinite(checkedAt)) return true;
76
+ return now - checkedAt >= TIME_ZONE_TTL_MS;
77
+ };
78
+
79
+ /**
80
+ * Merge a freshly read zone into a credential's metadata.
81
+ *
82
+ * Merges rather than replaces, because this column also carries the reauth
83
+ * markers — writing a bare `{ timeZone }` here would clear `needsReauth` and
84
+ * silently re-enable a connection the refresh path had flagged as broken.
85
+ *
86
+ * A null zone (the vendor could not tell us) still stamps the check, so a
87
+ * provider that never reports one is asked once a day rather than on every
88
+ * single call.
89
+ */
90
+ export const writeCredentialTimeZone = (
91
+ previous: unknown,
92
+ timeZone: string | null,
93
+ now: Date = new Date()
94
+ ): Record<string, unknown> => {
95
+ const base =
96
+ previous && typeof previous === 'object'
97
+ ? { ...(previous as Record<string, unknown>) }
98
+ : {};
99
+ if (isValidTimeZone(timeZone)) {
100
+ base[TIME_ZONE_KEY] = timeZone.trim();
101
+ } else {
102
+ delete base[TIME_ZONE_KEY];
103
+ }
104
+ base[TIME_ZONE_CHECKED_AT_KEY] = now.toISOString();
105
+ return base;
106
+ };
107
+
108
+ /**
109
+ * The zone Google Calendar is configured in, from the primary calendar.
110
+ *
111
+ * Deliberately NOT `GET /users/me/settings/timezone`, which is the more
112
+ * direct answer to "what did the user configure" and needs
113
+ * `calendar.settings.readonly` — a scope we do not request and could not add
114
+ * without sending every already-connected user back through consent. The
115
+ * primary calendar's zone is the same value in every case that matters, and
116
+ * `calendar.readonly` already covers it.
117
+ *
118
+ * Returns null on any failure. Not knowing the zone is a state the callers
119
+ * handle; a throw here would take a tool call or a chat turn with it.
120
+ */
121
+ export const fetchGoogleCalendarTimeZone = async (
122
+ accessToken: string
123
+ ): Promise<string | null> => {
124
+ try {
125
+ const response = await fetch(
126
+ `${constants.GOOGLE_CALENDAR_API_BASE}/calendars/primary`,
127
+ {
128
+ headers: {
129
+ Authorization: `Bearer ${accessToken}`,
130
+ Accept: 'application/json'
131
+ }
132
+ }
133
+ );
134
+ if (!response.ok) return null;
135
+ const payload = (await response.json()) as { timeZone?: unknown };
136
+ return isValidTimeZone(payload?.timeZone) ? payload.timeZone.trim() : null;
137
+ } catch {
138
+ return null;
139
+ }
140
+ };
141
+
142
+ /**
143
+ * The zone on the connected Cal.com profile.
144
+ *
145
+ * This is the host's zone — the one their availability is written in — which
146
+ * is what "9am" means when the artifact owner or their bot says it. It is not
147
+ * the attendee's zone; see the booking handler for why we use it there anyway.
148
+ *
149
+ * Same null-on-failure contract as the Google reader above.
150
+ */
151
+ export const fetchCalcomTimeZone = async (
152
+ apiKey: string
153
+ ): Promise<string | null> => {
154
+ try {
155
+ const response = await fetch(`${constants.CALCOM_API_BASE}/me`, {
156
+ headers: {
157
+ Authorization: `Bearer ${apiKey}`,
158
+ 'cal-api-version': constants.CALCOM_API_VERSION_ME,
159
+ Accept: 'application/json'
160
+ }
161
+ });
162
+ if (!response.ok) return null;
163
+ const payload = (await response.json()) as {
164
+ data?: { timeZone?: unknown };
165
+ };
166
+ const value = payload?.data?.timeZone;
167
+ return isValidTimeZone(value) ? value.trim() : null;
168
+ } catch {
169
+ return null;
170
+ }
171
+ };