@cloudstreamsoftware/claude-tools 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +152 -37
  2. package/agents/INDEX.md +183 -0
  3. package/agents/architect.md +247 -0
  4. package/agents/build-error-resolver.md +555 -0
  5. package/agents/catalyst-deployer.md +132 -0
  6. package/agents/code-reviewer.md +121 -0
  7. package/agents/compliance-auditor.md +148 -0
  8. package/agents/creator-architect.md +395 -0
  9. package/agents/deluge-reviewer.md +98 -0
  10. package/agents/doc-updater.md +471 -0
  11. package/agents/e2e-runner.md +711 -0
  12. package/agents/planner.md +122 -0
  13. package/agents/refactor-cleaner.md +309 -0
  14. package/agents/security-reviewer.md +582 -0
  15. package/agents/tdd-guide.md +302 -0
  16. package/config/versions.json +63 -0
  17. package/dist/hooks/hooks.json +209 -0
  18. package/dist/index.js +47 -0
  19. package/dist/lib/asset-value.js +609 -0
  20. package/dist/lib/client-manager.js +300 -0
  21. package/dist/lib/command-matcher.js +242 -0
  22. package/dist/lib/cross-session-patterns.js +754 -0
  23. package/dist/lib/intent-classifier.js +1075 -0
  24. package/dist/lib/package-manager.js +374 -0
  25. package/dist/lib/recommendation-engine.js +597 -0
  26. package/dist/lib/session-memory.js +489 -0
  27. package/dist/lib/skill-effectiveness.js +486 -0
  28. package/dist/lib/skill-matcher.js +595 -0
  29. package/dist/lib/tutorial-metrics.js +242 -0
  30. package/dist/lib/tutorial-progress.js +209 -0
  31. package/dist/lib/tutorial-renderer.js +431 -0
  32. package/dist/lib/utils.js +380 -0
  33. package/dist/lib/verify-formatter.js +143 -0
  34. package/dist/lib/workflow-state.js +249 -0
  35. package/hooks/hooks.json +209 -0
  36. package/package.json +5 -1
  37. package/scripts/aggregate-sessions.js +290 -0
  38. package/scripts/branch-name-validator.js +291 -0
  39. package/scripts/build.js +101 -0
  40. package/scripts/commands/client-switch.js +231 -0
  41. package/scripts/deprecate-skill.js +610 -0
  42. package/scripts/diagnose.js +324 -0
  43. package/scripts/doc-freshness.js +168 -0
  44. package/scripts/generate-weekly-digest.js +393 -0
  45. package/scripts/health-check.js +270 -0
  46. package/scripts/hooks/credential-check.js +101 -0
  47. package/scripts/hooks/evaluate-session.js +81 -0
  48. package/scripts/hooks/pre-compact.js +66 -0
  49. package/scripts/hooks/prompt-analyzer.js +276 -0
  50. package/scripts/hooks/prompt-router.js +422 -0
  51. package/scripts/hooks/quality-gate-enforcer.js +371 -0
  52. package/scripts/hooks/session-end.js +156 -0
  53. package/scripts/hooks/session-start.js +195 -0
  54. package/scripts/hooks/skill-injector.js +333 -0
  55. package/scripts/hooks/suggest-compact.js +58 -0
  56. package/scripts/lib/asset-value.js +609 -0
  57. package/scripts/lib/client-manager.js +300 -0
  58. package/scripts/lib/command-matcher.js +242 -0
  59. package/scripts/lib/cross-session-patterns.js +754 -0
  60. package/scripts/lib/intent-classifier.js +1075 -0
  61. package/scripts/lib/package-manager.js +374 -0
  62. package/scripts/lib/recommendation-engine.js +597 -0
  63. package/scripts/lib/session-memory.js +489 -0
  64. package/scripts/lib/skill-effectiveness.js +486 -0
  65. package/scripts/lib/skill-matcher.js +595 -0
  66. package/scripts/lib/tutorial-metrics.js +242 -0
  67. package/scripts/lib/tutorial-progress.js +209 -0
  68. package/scripts/lib/tutorial-renderer.js +431 -0
  69. package/scripts/lib/utils.js +380 -0
  70. package/scripts/lib/verify-formatter.js +143 -0
  71. package/scripts/lib/workflow-state.js +249 -0
  72. package/scripts/onboard.js +363 -0
  73. package/scripts/quarterly-report.js +692 -0
  74. package/scripts/setup-package-manager.js +204 -0
  75. package/scripts/sync-upstream.js +391 -0
  76. package/scripts/test.js +108 -0
  77. package/scripts/tutorial-runner.js +351 -0
  78. package/scripts/validate-all.js +201 -0
  79. package/scripts/verifiers/agents.js +245 -0
  80. package/scripts/verifiers/config.js +186 -0
  81. package/scripts/verifiers/environment.js +123 -0
  82. package/scripts/verifiers/hooks.js +188 -0
  83. package/scripts/verifiers/index.js +38 -0
  84. package/scripts/verifiers/persistence.js +140 -0
  85. package/scripts/verifiers/plugin.js +215 -0
  86. package/scripts/verifiers/skills.js +209 -0
  87. package/scripts/verify-setup.js +164 -0
  88. package/skills/INDEX.md +157 -0
  89. package/skills/backend-patterns/SKILL.md +586 -0
  90. package/skills/backend-patterns/catalyst-patterns.md +128 -0
  91. package/skills/bigquery-patterns/SKILL.md +27 -0
  92. package/skills/bigquery-patterns/performance-optimization.md +518 -0
  93. package/skills/bigquery-patterns/query-patterns.md +372 -0
  94. package/skills/bigquery-patterns/schema-design.md +78 -0
  95. package/skills/cloudstream-project-template/SKILL.md +20 -0
  96. package/skills/cloudstream-project-template/structure.md +65 -0
  97. package/skills/coding-standards/SKILL.md +524 -0
  98. package/skills/coding-standards/deluge-standards.md +83 -0
  99. package/skills/compliance-patterns/SKILL.md +28 -0
  100. package/skills/compliance-patterns/hipaa/audit-requirements.md +251 -0
  101. package/skills/compliance-patterns/hipaa/baa-process.md +298 -0
  102. package/skills/compliance-patterns/hipaa/data-archival-strategy.md +387 -0
  103. package/skills/compliance-patterns/hipaa/phi-handling.md +52 -0
  104. package/skills/compliance-patterns/pci-dss/saq-a-requirements.md +307 -0
  105. package/skills/compliance-patterns/pci-dss/tokenization-patterns.md +382 -0
  106. package/skills/compliance-patterns/pci-dss/zoho-checkout-patterns.md +56 -0
  107. package/skills/compliance-patterns/soc2/access-controls.md +344 -0
  108. package/skills/compliance-patterns/soc2/audit-logging.md +458 -0
  109. package/skills/compliance-patterns/soc2/change-management.md +403 -0
  110. package/skills/compliance-patterns/soc2/deluge-execution-logging.md +407 -0
  111. package/skills/consultancy-workflows/SKILL.md +19 -0
  112. package/skills/consultancy-workflows/client-isolation.md +21 -0
  113. package/skills/consultancy-workflows/documentation-automation.md +454 -0
  114. package/skills/consultancy-workflows/handoff-procedures.md +257 -0
  115. package/skills/consultancy-workflows/knowledge-capture.md +513 -0
  116. package/skills/consultancy-workflows/time-tracking.md +26 -0
  117. package/skills/continuous-learning/SKILL.md +84 -0
  118. package/skills/continuous-learning/config.json +18 -0
  119. package/skills/continuous-learning/evaluate-session.sh +60 -0
  120. package/skills/continuous-learning-v2/SKILL.md +126 -0
  121. package/skills/continuous-learning-v2/config.json +61 -0
  122. package/skills/frontend-patterns/SKILL.md +635 -0
  123. package/skills/frontend-patterns/zoho-widget-patterns.md +103 -0
  124. package/skills/gcp-data-engineering/SKILL.md +36 -0
  125. package/skills/gcp-data-engineering/bigquery/performance-optimization.md +337 -0
  126. package/skills/gcp-data-engineering/dataflow/error-handling.md +496 -0
  127. package/skills/gcp-data-engineering/dataflow/pipeline-patterns.md +444 -0
  128. package/skills/gcp-data-engineering/dbt/model-organization.md +63 -0
  129. package/skills/gcp-data-engineering/dbt/testing-patterns.md +503 -0
  130. package/skills/gcp-data-engineering/medallion-architecture/bronze-layer.md +60 -0
  131. package/skills/gcp-data-engineering/medallion-architecture/gold-layer.md +311 -0
  132. package/skills/gcp-data-engineering/medallion-architecture/layer-transitions.md +517 -0
  133. package/skills/gcp-data-engineering/medallion-architecture/silver-layer.md +305 -0
  134. package/skills/gcp-data-engineering/zoho-to-gcp/data-extraction.md +543 -0
  135. package/skills/gcp-data-engineering/zoho-to-gcp/real-time-vs-batch.md +337 -0
  136. package/skills/security-review/SKILL.md +498 -0
  137. package/skills/security-review/compliance-checklist.md +53 -0
  138. package/skills/strategic-compact/SKILL.md +67 -0
  139. package/skills/tdd-workflow/SKILL.md +413 -0
  140. package/skills/tdd-workflow/zoho-testing.md +124 -0
  141. package/skills/tutorial/SKILL.md +249 -0
  142. package/skills/tutorial/docs/ACCESSIBILITY.md +169 -0
  143. package/skills/tutorial/lessons/00-philosophy-and-workflow.md +198 -0
  144. package/skills/tutorial/lessons/01-basics.md +81 -0
  145. package/skills/tutorial/lessons/02-training.md +86 -0
  146. package/skills/tutorial/lessons/03-commands.md +109 -0
  147. package/skills/tutorial/lessons/04-workflows.md +115 -0
  148. package/skills/tutorial/lessons/05-compliance.md +116 -0
  149. package/skills/tutorial/lessons/06-zoho.md +121 -0
  150. package/skills/tutorial/lessons/07-hooks-system.md +277 -0
  151. package/skills/tutorial/lessons/08-mcp-servers.md +316 -0
  152. package/skills/tutorial/lessons/09-client-management.md +215 -0
  153. package/skills/tutorial/lessons/10-testing-e2e.md +260 -0
  154. package/skills/tutorial/lessons/11-skills-deep-dive.md +272 -0
  155. package/skills/tutorial/lessons/12-rules-system.md +326 -0
  156. package/skills/tutorial/lessons/13-golden-standard-graduation.md +213 -0
  157. package/skills/tutorial/lessons/14-fork-setup-and-sync.md +312 -0
  158. package/skills/tutorial/lessons/15-living-examples-system.md +221 -0
  159. package/skills/tutorial/tracks/accelerated/README.md +134 -0
  160. package/skills/tutorial/tracks/accelerated/assessment/checkpoint-1.md +161 -0
  161. package/skills/tutorial/tracks/accelerated/assessment/checkpoint-2.md +175 -0
  162. package/skills/tutorial/tracks/accelerated/day-1-core-concepts.md +234 -0
  163. package/skills/tutorial/tracks/accelerated/day-2-essential-commands.md +270 -0
  164. package/skills/tutorial/tracks/accelerated/day-3-workflow-mastery.md +305 -0
  165. package/skills/tutorial/tracks/accelerated/day-4-compliance-zoho.md +304 -0
  166. package/skills/tutorial/tracks/accelerated/day-5-hooks-skills.md +344 -0
  167. package/skills/tutorial/tracks/accelerated/day-6-client-testing.md +386 -0
  168. package/skills/tutorial/tracks/accelerated/day-7-graduation.md +369 -0
  169. package/skills/zoho-patterns/CHANGELOG.md +108 -0
  170. package/skills/zoho-patterns/SKILL.md +446 -0
  171. package/skills/zoho-patterns/analytics/dashboard-patterns.md +352 -0
  172. package/skills/zoho-patterns/analytics/zoho-to-bigquery-pipeline.md +427 -0
  173. package/skills/zoho-patterns/catalyst/appsail-deployment.md +349 -0
  174. package/skills/zoho-patterns/catalyst/context-close-patterns.md +354 -0
  175. package/skills/zoho-patterns/catalyst/cron-batch-processing.md +374 -0
  176. package/skills/zoho-patterns/catalyst/function-patterns.md +439 -0
  177. package/skills/zoho-patterns/creator/form-design.md +304 -0
  178. package/skills/zoho-patterns/creator/publish-api-patterns.md +313 -0
  179. package/skills/zoho-patterns/creator/widget-integration.md +306 -0
  180. package/skills/zoho-patterns/creator/workflow-automation.md +253 -0
  181. package/skills/zoho-patterns/deluge/api-patterns.md +468 -0
  182. package/skills/zoho-patterns/deluge/batch-processing.md +403 -0
  183. package/skills/zoho-patterns/deluge/cross-app-integration.md +356 -0
  184. package/skills/zoho-patterns/deluge/error-handling.md +423 -0
  185. package/skills/zoho-patterns/deluge/syntax-reference.md +65 -0
  186. package/skills/zoho-patterns/integration/cors-proxy-architecture.md +426 -0
  187. package/skills/zoho-patterns/integration/crm-books-native-sync.md +277 -0
  188. package/skills/zoho-patterns/integration/oauth-token-management.md +461 -0
  189. package/skills/zoho-patterns/integration/zoho-flow-patterns.md +334 -0
@@ -0,0 +1,461 @@
1
+ # OAuth Token Management
2
+
3
+ ## Zoho OAuth Token Lifecycle
4
+
5
+ > **CRITICAL:** Zoho access tokens expire in exactly 1 hour (3600 seconds). You MUST implement refresh logic for any standalone application. Deluge Connections handle this automatically.
6
+
7
+ ```
8
+ Authorization Flow:
9
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
10
+ │ User grants │ → │ Auth code │ → │ Access token │
11
+ │ permission │ │ (10 min TTL) │ │ (1 hour TTL) │
12
+ └──────────────┘ └──────────────┘ └──────┬───────┘
13
+ │ Expires
14
+
15
+ ┌──────────────┐
16
+ │ Refresh token│ → New access token
17
+ │ (no expiry*) │
18
+ └──────────────┘
19
+ * Refresh tokens don't expire but can be revoked
20
+ ```
21
+
22
+ ## Using Connections in Deluge (Preferred)
23
+
24
+ Connections handle token refresh automatically. This is always the preferred approach within Zoho apps:
25
+
26
+ ```deluge
27
+ // Connections manage the full OAuth lifecycle
28
+ // You NEVER see or handle tokens directly
29
+ response = invokeUrl [
30
+ url: "https://www.zohoapis.com/crm/v5/Deals"
31
+ type: GET
32
+ connection: "zoho-crm-connection"
33
+ ];
34
+
35
+ // The connection handles:
36
+ // 1. Storing the access token
37
+ // 2. Detecting expiry
38
+ // 3. Using refresh token to get new access token
39
+ // 4. Retrying the request with new token
40
+ ```
41
+
42
+ ### Creating a Connection
43
+
44
+ 1. Go to Setup > Developer > Connections
45
+ 2. Select service (Zoho CRM, Books, etc.) or Custom OAuth
46
+ 3. Define required scopes
47
+ 4. Click "Create and Connect"
48
+ 5. Authorize in popup
49
+ 6. Use connection name in `invokeUrl`
50
+
51
+ ### Connection Scopes (Minimum Necessary)
52
+
53
+ | Zoho App | Common Scopes | When Needed |
54
+ |----------|---------------|-------------|
55
+ | CRM | `ZohoCRM.modules.ALL` | Full module access |
56
+ | CRM | `ZohoCRM.modules.READ` | Read-only reporting |
57
+ | Books | `ZohoBooks.fullaccess.all` | Full Books access |
58
+ | Books | `ZohoBooks.invoices.READ` | Invoice queries only |
59
+ | Creator | `ZohoCreator.form.CREATE` | Form submissions |
60
+ | Analytics | `ZohoAnalytics.fullaccess.all` | Reports and data |
61
+
62
+ > **WARNING:** Use the minimum scopes needed. Over-scoping creates security risk. For SOC2 clients, document scope justification.
63
+
64
+ ### When Connections Fail
65
+
66
+ ```deluge
67
+ try
68
+ {
69
+ response = invokeUrl [
70
+ url: "https://www.zohoapis.com/crm/v5/Deals"
71
+ type: GET
72
+ connection: "zoho-crm-connection"
73
+ ];
74
+ }
75
+ catch (e)
76
+ {
77
+ errorStr = e.toString();
78
+
79
+ if (errorStr.contains("INVALID_TOKEN") || errorStr.contains("INVALID_OAUTH"))
80
+ {
81
+ // Connection needs reauthorization
82
+ sendmail [
83
+ from: zoho.adminuserid
84
+ to: "admin@company.com"
85
+ subject: "[URGENT] Zoho Connection Expired - Reauthorize Required"
86
+ message: "Connection 'zoho-crm-connection' needs reauthorization. Go to Setup > Connections to fix."
87
+ ];
88
+ }
89
+ else
90
+ {
91
+ info "API error (non-auth): " + errorStr;
92
+ }
93
+ }
94
+ ```
95
+
96
+ ## Manual Refresh Token Flow (Standalone Apps)
97
+
98
+ For Node.js, Python, or other standalone applications outside Zoho:
99
+
100
+ ### Step 1: Initial Authorization
101
+
102
+ ```
103
+ GET https://accounts.zoho.com/oauth/v2/auth?
104
+ scope=ZohoCRM.modules.ALL&
105
+ client_id=YOUR_CLIENT_ID&
106
+ response_type=code&
107
+ access_type=offline&
108
+ redirect_uri=https://your-app.com/callback&
109
+ prompt=consent
110
+ ```
111
+
112
+ ### Step 2: Exchange Code for Tokens
113
+
114
+ ```javascript
115
+ const axios = require("axios");
116
+
117
+ async function exchangeCodeForTokens(authCode) {
118
+ const response = await axios.post("https://accounts.zoho.com/oauth/v2/token", null, {
119
+ params: {
120
+ grant_type: "authorization_code",
121
+ client_id: process.env.ZOHO_CLIENT_ID,
122
+ client_secret: process.env.ZOHO_CLIENT_SECRET,
123
+ redirect_uri: process.env.ZOHO_REDIRECT_URI,
124
+ code: authCode
125
+ }
126
+ });
127
+
128
+ // Response contains:
129
+ // { access_token, refresh_token, expires_in: 3600, token_type: "Bearer" }
130
+ return response.data;
131
+ }
132
+ ```
133
+
134
+ ### Step 3: Refresh Token (Automated)
135
+
136
+ ```javascript
137
+ class ZohoTokenManager {
138
+ constructor(clientId, clientSecret, refreshToken, dcDomain = "com") {
139
+ this.clientId = clientId;
140
+ this.clientSecret = clientSecret;
141
+ this.refreshToken = refreshToken;
142
+ this.accountsUrl = `https://accounts.zoho.${dcDomain}/oauth/v2/token`;
143
+ this.accessToken = null;
144
+ this.expiresAt = 0;
145
+ }
146
+
147
+ async getAccessToken() {
148
+ // Refresh proactively at 50 minutes (before 60-minute expiry)
149
+ const bufferMs = 10 * 60 * 1000; // 10-minute buffer
150
+ if (this.accessToken && Date.now() < this.expiresAt - bufferMs) {
151
+ return this.accessToken;
152
+ }
153
+
154
+ try {
155
+ const response = await axios.post(this.accountsUrl, null, {
156
+ params: {
157
+ refresh_token: this.refreshToken,
158
+ client_id: this.clientId,
159
+ client_secret: this.clientSecret,
160
+ grant_type: "refresh_token"
161
+ }
162
+ });
163
+
164
+ if (response.data.error) {
165
+ throw new Error(`Token refresh failed: ${response.data.error}`);
166
+ }
167
+
168
+ this.accessToken = response.data.access_token;
169
+ this.expiresAt = Date.now() + (response.data.expires_in * 1000);
170
+
171
+ return this.accessToken;
172
+ } catch (error) {
173
+ console.error("Token refresh error:", error.message);
174
+ throw error;
175
+ }
176
+ }
177
+
178
+ // Make authenticated API call
179
+ async apiCall(url, method = "GET", data = null) {
180
+ const token = await this.getAccessToken();
181
+
182
+ const config = {
183
+ method,
184
+ url,
185
+ headers: {
186
+ "Authorization": `Zoho-oauthtoken ${token}`,
187
+ "Content-Type": "application/json"
188
+ }
189
+ };
190
+
191
+ if (data) config.data = data;
192
+
193
+ try {
194
+ return await axios(config);
195
+ } catch (error) {
196
+ // If 401, force token refresh and retry once
197
+ if (error.response && error.response.status === 401) {
198
+ this.accessToken = null; // Force refresh
199
+ const newToken = await this.getAccessToken();
200
+ config.headers["Authorization"] = `Zoho-oauthtoken ${newToken}`;
201
+ return await axios(config);
202
+ }
203
+ throw error;
204
+ }
205
+ }
206
+ }
207
+
208
+ // Usage
209
+ const zoho = new ZohoTokenManager(
210
+ process.env.ZOHO_CLIENT_ID,
211
+ process.env.ZOHO_CLIENT_SECRET,
212
+ process.env.ZOHO_REFRESH_TOKEN,
213
+ process.env.ZOHO_DC || "com"
214
+ );
215
+
216
+ const deals = await zoho.apiCall("https://www.zohoapis.com/crm/v5/Deals");
217
+ ```
218
+
219
+ ## Token Storage Best Practices
220
+
221
+ ### Where to Store Tokens
222
+
223
+ | Storage | Appropriate For | Security Level |
224
+ |---------|----------------|----------------|
225
+ | Catalyst Segments | Catalyst functions | Encrypted at rest |
226
+ | Environment variables | Server-side apps | Process-level isolation |
227
+ | Secret Manager (GCP/AWS) | Production systems | Full audit trail |
228
+ | .env file (local) | Development only | Gitignored |
229
+
230
+ ### Where NEVER to Store Tokens
231
+
232
+ - Deluge variables or Creator fields
233
+ - Git repositories (even private)
234
+ - Client-side JavaScript
235
+ - Browser localStorage/cookies
236
+ - Log files
237
+ - Email or chat messages
238
+ - Hard-coded in source code
239
+
240
+ ### Catalyst Segments Storage
241
+
242
+ ```javascript
243
+ // Store token in Catalyst Segments (encrypted key-value store)
244
+ const catalyst = require("zcatalyst-sdk-node");
245
+
246
+ async function storeToken(context, key, tokenData) {
247
+ const app = catalyst.initialize(context);
248
+ const segment = app.cache().segment();
249
+
250
+ await segment.put(key, JSON.stringify({
251
+ access_token: tokenData.access_token,
252
+ expires_at: Date.now() + (tokenData.expires_in * 1000),
253
+ stored_at: new Date().toISOString()
254
+ }), tokenData.expires_in); // TTL matches token expiry
255
+
256
+ return true;
257
+ }
258
+
259
+ async function getToken(context, key) {
260
+ const app = catalyst.initialize(context);
261
+ const segment = app.cache().segment();
262
+
263
+ const cached = await segment.get(key);
264
+ if (cached) {
265
+ const data = JSON.parse(cached.cache_value);
266
+ if (Date.now() < data.expires_at) {
267
+ return data.access_token;
268
+ }
269
+ }
270
+ return null; // Token expired or not found
271
+ }
272
+ ```
273
+
274
+ ## Multi-DC Token Management
275
+
276
+ Zoho operates in multiple data centers. Tokens are DC-specific:
277
+
278
+ | DC | Accounts URL | API Domain | Region |
279
+ |----|-------------|------------|--------|
280
+ | com | accounts.zoho.com | zohoapis.com | US |
281
+ | eu | accounts.zoho.eu | zohoapis.eu | Europe |
282
+ | in | accounts.zoho.in | zohoapis.in | India |
283
+ | com.au | accounts.zoho.com.au | zohoapis.com.au | Australia |
284
+ | jp | accounts.zoho.jp | zohoapis.jp | Japan |
285
+ | com.cn | accounts.zoho.com.cn | zohoapis.com.cn | China |
286
+ | ca | accounts.zohocloud.ca | zohoapis.ca | Canada |
287
+
288
+ ### Multi-DC Implementation
289
+
290
+ ```javascript
291
+ // Determine correct DC based on client configuration
292
+ function getZohoUrls(dc) {
293
+ const dcMap = {
294
+ "com": { accounts: "accounts.zoho.com", api: "zohoapis.com" },
295
+ "eu": { accounts: "accounts.zoho.eu", api: "zohoapis.eu" },
296
+ "in": { accounts: "accounts.zoho.in", api: "zohoapis.in" },
297
+ "com.au": { accounts: "accounts.zoho.com.au", api: "zohoapis.com.au" },
298
+ "jp": { accounts: "accounts.zoho.jp", api: "zohoapis.jp" },
299
+ "ca": { accounts: "accounts.zohocloud.ca", api: "zohoapis.ca" }
300
+ };
301
+
302
+ if (!dcMap[dc]) {
303
+ throw new Error(`Unknown Zoho DC: ${dc}. Valid: ${Object.keys(dcMap).join(", ")}`);
304
+ }
305
+
306
+ return {
307
+ tokenUrl: `https://${dcMap[dc].accounts}/oauth/v2/token`,
308
+ authUrl: `https://${dcMap[dc].accounts}/oauth/v2/auth`,
309
+ apiBase: `https://www.${dcMap[dc].api}`
310
+ };
311
+ }
312
+
313
+ // Per-client token manager
314
+ class MultiDCTokenManager {
315
+ constructor(clientConfigs) {
316
+ // clientConfigs: { clientId: { dc, clientId, clientSecret, refreshToken } }
317
+ this.managers = {};
318
+
319
+ for (const [id, config] of Object.entries(clientConfigs)) {
320
+ this.managers[id] = new ZohoTokenManager(
321
+ config.clientId,
322
+ config.clientSecret,
323
+ config.refreshToken,
324
+ config.dc
325
+ );
326
+ }
327
+ }
328
+
329
+ async getClientToken(clientId) {
330
+ if (!this.managers[clientId]) {
331
+ throw new Error(`No token manager for client: ${clientId}`);
332
+ }
333
+ return await this.managers[clientId].getAccessToken();
334
+ }
335
+ }
336
+ ```
337
+
338
+ ## Error Handling for Expired Tokens
339
+
340
+ ### Common Token Errors
341
+
342
+ | Error Code | Meaning | Action |
343
+ |-----------|---------|--------|
344
+ | INVALID_TOKEN | Access token expired | Refresh using refresh token |
345
+ | INVALID_OAUTH_TOKEN | Token malformed | Check token storage, regenerate |
346
+ | OAUTH_SCOPE_MISMATCH | Insufficient scope | Re-authorize with correct scopes |
347
+ | INVALID_CLIENT | Client ID/Secret wrong | Verify API console credentials |
348
+ | INVALID_CODE | Auth code expired (>10 min) | Restart auth flow |
349
+ | ACCESS_DENIED | User revoked access | Re-authorize, user must grant again |
350
+
351
+ ### Handling Token Errors in Production
352
+
353
+ ```javascript
354
+ async function handleTokenError(error, clientId) {
355
+ const errorCode = error.response?.data?.code || error.message;
356
+
357
+ switch (errorCode) {
358
+ case "INVALID_TOKEN":
359
+ // Normal expiry - auto-refresh handles this
360
+ console.log("Token expired, refreshing...");
361
+ return await tokenManager.forceRefresh(clientId);
362
+
363
+ case "INVALID_OAUTH_TOKEN":
364
+ case "INVALID_CLIENT":
365
+ // Credentials issue - needs human intervention
366
+ await notifyAdmin({
367
+ subject: `[CRITICAL] OAuth credentials invalid for ${clientId}`,
368
+ message: `Error: ${errorCode}. Manual reauthorization required.`
369
+ });
370
+ throw new Error("OAuth credentials invalid - manual fix required");
371
+
372
+ case "OAUTH_SCOPE_MISMATCH":
373
+ // Scope changed - need new authorization
374
+ await notifyAdmin({
375
+ subject: `[ACTION] OAuth scope change needed for ${clientId}`,
376
+ message: `Current scopes insufficient. Re-authorize with updated scopes.`
377
+ });
378
+ throw new Error("Insufficient OAuth scopes");
379
+
380
+ case "ACCESS_DENIED":
381
+ // User revoked - completely broken until re-authorized
382
+ await notifyAdmin({
383
+ subject: `[CRITICAL] OAuth access revoked for ${clientId}`,
384
+ message: `User has revoked access. All API calls will fail until re-authorized.`
385
+ });
386
+ throw new Error("OAuth access revoked by user");
387
+
388
+ default:
389
+ throw error;
390
+ }
391
+ }
392
+ ```
393
+
394
+ ## Multi-Org Token Management (Consultancy Pattern)
395
+
396
+ For CloudStream with multiple client orgs:
397
+
398
+ ```javascript
399
+ // .env.client file structure (gitignored)
400
+ // ZOHO_DC=com
401
+ // ZOHO_CLIENT_ID=1000.ABC123...
402
+ // ZOHO_CLIENT_SECRET=xyz789...
403
+ // ZOHO_REFRESH_TOKEN=1000.refresh...
404
+ // ZOHO_ORG_ID=12345678
405
+
406
+ // Client switch loads correct credentials
407
+ // Use /client-switch command to swap active client context
408
+
409
+ // Token isolation: Each client has completely separate OAuth app
410
+ // Never share refresh tokens between clients
411
+ // Store per-client in separate env files or secret manager keys
412
+ ```
413
+
414
+ ### Token Rotation Schedule
415
+
416
+ For SOC2/HIPAA compliance:
417
+
418
+ ```
419
+ Monthly:
420
+ 1. Generate new client_secret in Zoho API Console
421
+ 2. Update all applications using that secret
422
+ 3. Verify token refresh works with new secret
423
+ 4. Revoke old secret after 24-hour verification period
424
+ 5. Document rotation in change log
425
+
426
+ On Incident:
427
+ 1. Immediately revoke compromised refresh token
428
+ 2. Generate new OAuth application if client_secret exposed
429
+ 3. Re-authorize all connections
430
+ 4. Audit access logs for unauthorized use
431
+ ```
432
+
433
+ ## Testing Token Management
434
+
435
+ ```javascript
436
+ // Verify token management is working correctly
437
+ async function tokenHealthCheck(clientId) {
438
+ try {
439
+ const token = await tokenManager.getAccessToken(clientId);
440
+
441
+ // Test the token with a simple API call
442
+ const response = await axios.get(
443
+ `${getZohoUrls(clientDC).apiBase}/crm/v5/org`,
444
+ { headers: { "Authorization": `Zoho-oauthtoken ${token}` } }
445
+ );
446
+
447
+ return {
448
+ status: "healthy",
449
+ org_name: response.data.org[0].company_name,
450
+ token_prefix: token.substring(0, 10) + "...",
451
+ dc: clientDC
452
+ };
453
+ } catch (error) {
454
+ return {
455
+ status: "unhealthy",
456
+ error: error.message,
457
+ action_required: true
458
+ };
459
+ }
460
+ }
461
+ ```