@zeyos/client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +458 -0
  4. package/agents/README.md +66 -0
  5. package/agents/shared/business-app-benchmarks.md +111 -0
  6. package/agents/shared/zeyos-entity-map.md +142 -0
  7. package/agents/shared/zeyos-entity-reference.md +570 -0
  8. package/agents/shared/zeyos-query-patterns.md +89 -0
  9. package/agents/zeyos-account-intelligence/SKILL.md +34 -0
  10. package/agents/zeyos-account-intelligence/agents/openai.yaml +4 -0
  11. package/agents/zeyos-account-intelligence/references/workflows.md +84 -0
  12. package/agents/zeyos-billing-insights/SKILL.md +41 -0
  13. package/agents/zeyos-billing-insights/agents/openai.yaml +4 -0
  14. package/agents/zeyos-billing-insights/references/workflows.md +106 -0
  15. package/agents/zeyos-campaign-and-outreach/SKILL.md +44 -0
  16. package/agents/zeyos-campaign-and-outreach/agents/openai.yaml +4 -0
  17. package/agents/zeyos-campaign-and-outreach/references/workflows.md +100 -0
  18. package/agents/zeyos-collaboration-and-activity/SKILL.md +37 -0
  19. package/agents/zeyos-collaboration-and-activity/agents/openai.yaml +4 -0
  20. package/agents/zeyos-collaboration-and-activity/references/workflows.md +104 -0
  21. package/agents/zeyos-collections-and-dunning/SKILL.md +46 -0
  22. package/agents/zeyos-collections-and-dunning/agents/openai.yaml +4 -0
  23. package/agents/zeyos-collections-and-dunning/references/workflows.md +132 -0
  24. package/agents/zeyos-commerce-and-inventory/SKILL.md +38 -0
  25. package/agents/zeyos-commerce-and-inventory/agents/openai.yaml +4 -0
  26. package/agents/zeyos-commerce-and-inventory/references/workflows.md +101 -0
  27. package/agents/zeyos-mail-operations/SKILL.md +35 -0
  28. package/agents/zeyos-mail-operations/agents/openai.yaml +4 -0
  29. package/agents/zeyos-mail-operations/references/workflows.md +110 -0
  30. package/agents/zeyos-notes-and-sops/SKILL.md +31 -0
  31. package/agents/zeyos-notes-and-sops/agents/openai.yaml +4 -0
  32. package/agents/zeyos-notes-and-sops/references/workflows.md +85 -0
  33. package/agents/zeyos-platform-and-schema/SKILL.md +37 -0
  34. package/agents/zeyos-platform-and-schema/agents/openai.yaml +4 -0
  35. package/agents/zeyos-platform-and-schema/references/workflows.md +97 -0
  36. package/agents/zeyos-work-management/SKILL.md +45 -0
  37. package/agents/zeyos-work-management/agents/openai.yaml +4 -0
  38. package/agents/zeyos-work-management/references/workflows.md +148 -0
  39. package/docs/01-api-reference/01-data-retrieval.md +601 -0
  40. package/docs/01-api-reference/02-authentication.md +288 -0
  41. package/docs/01-api-reference/03-resources.md +270 -0
  42. package/docs/01-api-reference/04-schema.md +539 -0
  43. package/docs/01-api-reference/_category_.json +9 -0
  44. package/docs/02-javascript-client/01-getting-started.md +146 -0
  45. package/docs/02-javascript-client/02-authentication.md +287 -0
  46. package/docs/02-javascript-client/03-making-requests.md +572 -0
  47. package/docs/02-javascript-client/04-practical-guide.md +348 -0
  48. package/docs/02-javascript-client/_category_.json +9 -0
  49. package/docs/03-cli/01-getting-started.md +219 -0
  50. package/docs/03-cli/02-commands.md +407 -0
  51. package/docs/03-cli/03-configuration.md +220 -0
  52. package/docs/03-cli/_category_.json +9 -0
  53. package/docs/04-agent-workflows/00-coding-agents.md +35 -0
  54. package/docs/04-agent-workflows/01-agent-quickstart.md +147 -0
  55. package/docs/04-agent-workflows/02-agent-recipes.md +109 -0
  56. package/docs/04-agent-workflows/03-cli-coverage-and-escalation.md +65 -0
  57. package/docs/04-agent-workflows/_category_.json +9 -0
  58. package/docs/04-sample-apps/01-kanban.md +89 -0
  59. package/docs/04-sample-apps/02-crm.md +81 -0
  60. package/docs/04-sample-apps/03-dashboard.md +80 -0
  61. package/docs/04-sample-apps/_category_.json +9 -0
  62. package/docs/05-tutorials/00-application-developers.md +43 -0
  63. package/docs/05-tutorials/01-integration-architecture.md +60 -0
  64. package/docs/05-tutorials/02-build-your-own-zeyos-frontend.md +517 -0
  65. package/docs/05-tutorials/03-server-side-integrations.md +185 -0
  66. package/docs/05-tutorials/_category_.json +9 -0
  67. package/docs/intro.md +197 -0
  68. package/openapi/api.json +24308 -0
  69. package/openapi/auth.json +415 -0
  70. package/openapi/dbref.json +56223 -0
  71. package/openapi/oauth2.json +781 -0
  72. package/openapi/sdk.json +949 -0
  73. package/openapi/views.txt +642 -0
  74. package/package.json +49 -0
  75. package/samples/crm/README.md +28 -0
  76. package/samples/crm/index.html +327 -0
  77. package/samples/crm/js/api.js +208 -0
  78. package/samples/crm/js/auth.js +61 -0
  79. package/samples/crm/js/main.js +545 -0
  80. package/samples/crm/js/state.js +90 -0
  81. package/samples/crm/js/ui.js +51 -0
  82. package/samples/dashboard/README.md +28 -0
  83. package/samples/dashboard/index.html +280 -0
  84. package/samples/dashboard/js/api.js +197 -0
  85. package/samples/dashboard/js/auth.js +59 -0
  86. package/samples/dashboard/js/main.js +382 -0
  87. package/samples/dashboard/js/state.js +81 -0
  88. package/samples/dashboard/js/ui.js +48 -0
  89. package/samples/kanban/README.md +28 -0
  90. package/samples/kanban/index.html +263 -0
  91. package/samples/kanban/js/api.js +152 -0
  92. package/samples/kanban/js/auth.js +59 -0
  93. package/samples/kanban/js/constants.js +40 -0
  94. package/samples/kanban/js/kanban.js +246 -0
  95. package/samples/kanban/js/main.js +362 -0
  96. package/samples/kanban/js/modals.js +474 -0
  97. package/samples/kanban/js/settings.js +82 -0
  98. package/samples/kanban/js/state.js +118 -0
  99. package/samples/kanban/js/ui.js +49 -0
  100. package/scripts/generate-client.mjs +344 -0
  101. package/src/generated/operations.js +9772 -0
  102. package/src/generated/schema.js +8982 -0
  103. package/src/index.js +85 -0
  104. package/src/runtime/client.js +1208 -0
  105. package/src/runtime/error.js +29 -0
  106. package/src/runtime/http.js +174 -0
  107. package/src/runtime/request-shape.js +35 -0
  108. package/src/runtime/schema.js +206 -0
  109. package/src/runtime/suggest.js +74 -0
  110. package/src/runtime/token-store.js +105 -0
@@ -0,0 +1,263 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ZeyOS Kanban</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <style>
9
+ /* Smooth drag-over highlight for drop zones */
10
+ .drop-active {
11
+ background: #dbeafe !important;
12
+ outline: 2px dashed #3b82f6;
13
+ outline-offset: -2px;
14
+ }
15
+ /* Clamp text to 2 lines */
16
+ .line-clamp-2 {
17
+ display: -webkit-box;
18
+ -webkit-line-clamp: 2;
19
+ -webkit-box-orient: vertical;
20
+ overflow: hidden;
21
+ }
22
+ /* Native dialog reset */
23
+ dialog { padding: 0; border: none; border-radius: 1rem; max-width: 90vw; }
24
+ dialog::backdrop { background: rgba(0,0,0,0.5); }
25
+
26
+ /* Thin scrollbar for column bodies */
27
+ .col-cards::-webkit-scrollbar { width: 4px; }
28
+ .col-cards::-webkit-scrollbar-track { background: transparent; }
29
+ .col-cards::-webkit-scrollbar-thumb { background: #e2e8f0; border-radius: 2px; }
30
+ </style>
31
+ </head>
32
+
33
+ <!--
34
+ ┌─────────────────────────────────────────────────────────────────────────┐
35
+ │ CONFIGURATION │
36
+ │ │
37
+ │ Set data-zeyos-url to your ZeyOS instance URL. │
38
+ │ Optionally provide tokens obtained via the CLI (zeyos login). │
39
+ │ │
40
+ │ Alternatively, use the browser console: │
41
+ │ ZeyOS.setUrl('https://cloud.zeyos.com/demo/') │
42
+ │ ZeyOS.setToken('your-access-token') │
43
+ │ ZeyOS.reconnect() │
44
+ │ │
45
+ │ Session mode only works from the same origin or with credentialed │
46
+ │ CORS enabled on the ZeyOS instance. Use token mode for localhost. │
47
+ └─────────────────────────────────────────────────────────────────────────┘
48
+ -->
49
+ <body class="bg-slate-100 min-h-screen font-sans antialiased"
50
+ data-zeyos-url=""
51
+ data-zeyos-accesstoken=""
52
+ data-zeyos-refreshtoken="">
53
+
54
+ <!-- ── Connection Screen ─────────────────────────────────────────────── -->
55
+ <div id="connection-screen" class="hidden min-h-screen flex items-center justify-center p-4">
56
+ <div class="bg-white rounded-2xl shadow-xl p-8 w-full max-w-md">
57
+
58
+ <!-- Logo / title -->
59
+ <div class="text-center mb-6">
60
+ <div class="w-14 h-14 rounded-2xl bg-blue-600 flex items-center justify-center mx-auto mb-4">
61
+ <svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
62
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
63
+ d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7" />
64
+ </svg>
65
+ </div>
66
+ <h1 class="text-2xl font-bold text-slate-900">ZeyOS Kanban</h1>
67
+ <p id="connection-message" class="text-slate-500 text-sm mt-1">Not connected</p>
68
+ </div>
69
+
70
+ <!-- Setup instructions -->
71
+ <div class="space-y-4 text-sm text-slate-600">
72
+
73
+ <div class="p-4 bg-blue-50 border border-blue-200 rounded-xl">
74
+ <h3 class="font-semibold text-blue-800 mb-2">Option 1 &mdash; Body Attributes</h3>
75
+ <p class="mb-2 leading-relaxed">Set connection details directly in <code class="text-xs bg-blue-100 px-1 py-0.5 rounded">index.html</code>:</p>
76
+ <pre class="text-xs bg-white p-3 rounded-lg border border-blue-100 overflow-x-auto leading-relaxed"><code>&lt;body
77
+ data-zeyos-url="https://cloud.zeyos.com/demo/"
78
+ data-zeyos-accesstoken="your-token"&gt;</code></pre>
79
+ </div>
80
+
81
+ <div class="p-4 bg-emerald-50 border border-emerald-200 rounded-xl">
82
+ <h3 class="font-semibold text-emerald-800 mb-2">Option 2 &mdash; Console API</h3>
83
+ <p class="mb-2 leading-relaxed">Open DevTools (<kbd class="text-xs bg-emerald-100 px-1.5 py-0.5 rounded font-mono">F12</kbd>) and run:</p>
84
+ <pre class="text-xs bg-white p-3 rounded-lg border border-emerald-100 overflow-x-auto leading-relaxed"><code>ZeyOS.setUrl('https://cloud.zeyos.com/demo/')
85
+ ZeyOS.setToken('your-access-token')
86
+ ZeyOS.reconnect()</code></pre>
87
+ </div>
88
+
89
+ <div class="p-4 bg-amber-50 border border-amber-200 rounded-xl">
90
+ <h3 class="font-semibold text-amber-800 mb-2">Option 3 &mdash; Session Mode</h3>
91
+ <p class="leading-relaxed">
92
+ Session mode only works from the same origin or when the ZeyOS instance
93
+ allows credentialed CORS. For localhost demos, use token mode.
94
+ </p>
95
+ </div>
96
+
97
+ <div class="p-3 bg-slate-50 border border-slate-200 rounded-xl text-xs text-slate-500 space-y-1.5">
98
+ <p>
99
+ <strong>Tip:</strong> Use the CLI to obtain a token with
100
+ <code class="bg-slate-100 px-1 py-0.5 rounded font-mono">zeyos login</code>, then explicitly print one with
101
+ <code class="bg-slate-100 px-1 py-0.5 rounded font-mono">zeyos whoami --show-token --json</code>
102
+ </p>
103
+ <p>
104
+ <strong>Debug:</strong> Run <code class="bg-slate-100 px-1 py-0.5 rounded font-mono">ZeyOS.status()</code>
105
+ in the console to see the current config.
106
+ </p>
107
+ </div>
108
+
109
+ </div>
110
+ </div>
111
+ </div>
112
+
113
+ <!-- ── App Shell ─────────────────────────────────────────────────────── -->
114
+ <div id="app-shell" class="hidden flex flex-col" style="height:100dvh">
115
+
116
+ <!-- Navbar -->
117
+ <header class="bg-white border-b border-slate-200 px-4 py-2.5 flex items-center gap-3 flex-shrink-0 shadow-sm">
118
+
119
+ <!-- Logo -->
120
+ <div class="flex items-center gap-2 mr-2">
121
+ <div class="w-7 h-7 rounded-lg bg-blue-600 flex items-center justify-center">
122
+ <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
123
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
124
+ d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7" />
125
+ </svg>
126
+ </div>
127
+ <span class="font-semibold text-slate-800 hidden sm:inline">ZeyOS Kanban</span>
128
+ </div>
129
+
130
+ <!-- Context selector -->
131
+ <button id="btn-context"
132
+ class="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-slate-100 hover:bg-slate-200 text-sm font-medium text-slate-700 transition-colors max-w-xs truncate">
133
+ <span id="context-label">All Tickets</span>
134
+ <svg class="w-3.5 h-3.5 text-slate-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
135
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
136
+ </svg>
137
+ </button>
138
+
139
+ <div class="flex-1"></div>
140
+
141
+ <!-- Reload button -->
142
+ <button id="btn-reload" title="Reload tickets"
143
+ class="p-2 rounded-lg hover:bg-slate-100 text-slate-500 hover:text-slate-700 transition-colors">
144
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
145
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
146
+ d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
147
+ </svg>
148
+ </button>
149
+
150
+ <!-- New ticket -->
151
+ <button id="btn-new-ticket"
152
+ class="flex items-center gap-1.5 px-3 py-1.5 bg-blue-600 text-white rounded-lg text-sm font-medium hover:bg-blue-700 active:bg-blue-800 transition-colors">
153
+ <span class="text-base leading-none">+</span>
154
+ <span class="hidden sm:inline">New Ticket</span>
155
+ </button>
156
+
157
+ <!-- Settings -->
158
+ <button id="btn-settings" title="Settings"
159
+ class="p-2 rounded-lg hover:bg-slate-100 text-slate-500 hover:text-slate-700 transition-colors">
160
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
161
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
162
+ d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
163
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
164
+ </svg>
165
+ </button>
166
+
167
+ <!-- Logout -->
168
+ <button id="btn-logout" title="Log out"
169
+ class="p-2 rounded-lg hover:bg-slate-100 text-slate-500 hover:text-red-500 transition-colors">
170
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
171
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
172
+ d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
173
+ </svg>
174
+ </button>
175
+
176
+ </header>
177
+
178
+ <!-- No-columns banner -->
179
+ <div id="no-columns-banner"
180
+ class="hidden bg-amber-50 border-b border-amber-200 px-4 py-2.5 flex items-center gap-3 text-sm flex-shrink-0">
181
+ <svg class="w-4 h-4 text-amber-500 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
182
+ <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
183
+ </svg>
184
+ <span class="text-amber-800">No columns configured yet — select which statuses to show as columns.</span>
185
+ <button id="btn-open-settings" class="font-medium text-amber-700 hover:text-amber-900 underline">Open Settings</button>
186
+ </div>
187
+
188
+ <!-- Kanban board -->
189
+ <main class="flex-1 overflow-x-auto overflow-y-hidden p-4">
190
+ <div id="kanban-board" class="flex gap-4 h-full min-w-max pb-2">
191
+ <!-- Columns injected by kanban.js -->
192
+ </div>
193
+ </main>
194
+
195
+ </div>
196
+
197
+ <!-- ── Context Dropdown ──────────────────────────────────────────────── -->
198
+ <div id="context-dropdown"
199
+ class="hidden fixed z-30 w-64 bg-white rounded-xl shadow-xl border border-slate-200 overflow-hidden">
200
+ <!-- Items injected by main.js -->
201
+ </div>
202
+
203
+ <!-- ── Settings Drawer ───────────────────────────────────────────────── -->
204
+ <div id="settings-overlay" class="hidden fixed inset-0 bg-black/20 z-40"></div>
205
+
206
+ <aside id="settings-panel"
207
+ class="hidden fixed inset-y-0 right-0 w-80 bg-white shadow-2xl z-50 flex flex-col border-l border-slate-200">
208
+
209
+ <div class="flex items-center justify-between px-5 py-4 border-b border-slate-200 flex-shrink-0">
210
+ <h2 class="font-semibold text-slate-800">Settings</h2>
211
+ <button id="btn-close-settings"
212
+ class="p-1.5 rounded-lg hover:bg-slate-100 text-slate-500 leading-none text-lg">&#x2715;</button>
213
+ </div>
214
+
215
+ <div class="flex-1 overflow-y-auto px-5 py-4 space-y-8">
216
+
217
+ <!-- Column Configuration -->
218
+ <section>
219
+ <h3 class="text-xs font-semibold uppercase tracking-widest text-slate-400 mb-3">Board Columns</h3>
220
+ <div id="columns-config">
221
+ <!-- Checkboxes rendered by settings.js -->
222
+ </div>
223
+ </section>
224
+
225
+ </div>
226
+ </aside>
227
+
228
+ <!-- ── Ticket Detail Modal ───────────────────────────────────────────── -->
229
+ <dialog id="ticket-detail-modal" style="width:min(680px,92vw)">
230
+ <!-- Content injected by modals.js -->
231
+ </dialog>
232
+
233
+ <!-- ── Ticket Form Modal ─────────────────────────────────────────────── -->
234
+ <dialog id="ticket-form-modal" style="width:min(520px,92vw)">
235
+ <!-- Content injected by modals.js -->
236
+ </dialog>
237
+
238
+ <!-- ── Toast Container ──────────────────────────────────────────────── -->
239
+ <div id="toast-container"
240
+ class="fixed bottom-5 right-5 z-50 flex flex-col gap-2 pointer-events-none">
241
+ </div>
242
+
243
+ <!-- ── Loading Overlay ──────────────────────────────────────────────── -->
244
+ <div id="loading-overlay"
245
+ class="hidden fixed inset-0 bg-white/60 backdrop-blur-sm z-50 flex items-center justify-center">
246
+ <div class="flex flex-col items-center gap-3">
247
+ <div class="w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"></div>
248
+ <span class="text-sm text-slate-600 font-medium">Loading&hellip;</span>
249
+ </div>
250
+ </div>
251
+
252
+ <script type="module" src="./js/main.js"></script>
253
+ <script>
254
+ // Wire the reload button outside the module so it works without import
255
+ document.addEventListener('DOMContentLoaded', () => {
256
+ document.getElementById('btn-reload')?.addEventListener('click', () => {
257
+ document.dispatchEvent(new CustomEvent('app:reload'));
258
+ });
259
+ });
260
+ </script>
261
+
262
+ </body>
263
+ </html>
@@ -0,0 +1,152 @@
1
+ /**
2
+ * ZeyOS API client singleton + high-level helper functions.
3
+ * Supports two initialization modes:
4
+ * - Token mode: uses pre-obtained browser tokens via MemoryTokenStore
5
+ * - Session mode: uses browser session cookies
6
+ */
7
+ import { createZeyosClient, MemoryTokenStore, normalizeListResult } from '../../../src/index.js';
8
+ import { loadTokens, saveTokens } from './state.js';
9
+
10
+ export let client = null;
11
+ export let tokenStore = null;
12
+
13
+ // ── Client Initialization ─────────────────────────────────────────────────
14
+
15
+ /**
16
+ * Initialize client in token/OAuth mode.
17
+ * Uses pre-obtained tokens from localStorage / body attributes.
18
+ */
19
+ export function initTokenClient(url) {
20
+ const stored = loadTokens();
21
+ tokenStore = new MemoryTokenStore(stored ?? undefined);
22
+
23
+ client = createZeyosClient({
24
+ platform: url,
25
+ auth: {
26
+ mode: 'oauth',
27
+ oauth: {
28
+ tokenStore,
29
+ },
30
+ },
31
+ });
32
+ return client;
33
+ }
34
+
35
+ /**
36
+ * Initialize client in session mode.
37
+ * Uses browser cookies via credentials:'include'.
38
+ * Works when the user is already logged into ZeyOS in the same browser.
39
+ */
40
+ export function initSessionClient(url) {
41
+ tokenStore = null;
42
+
43
+ client = createZeyosClient({
44
+ platform: url,
45
+ auth: {
46
+ mode: 'session',
47
+ session: {
48
+ enabled: true,
49
+ credentials: 'include',
50
+ },
51
+ },
52
+ });
53
+ return client;
54
+ }
55
+
56
+ /** Persist the current token set back to localStorage (no-op in session mode). */
57
+ export async function syncTokens() {
58
+ if (!tokenStore) return; // session mode — no tokens to sync
59
+ try {
60
+ const ts = await client.auth.getTokenSet();
61
+ if (ts?.accessToken) {
62
+ saveTokens({
63
+ accessToken: ts.accessToken,
64
+ refreshToken: ts.refreshToken,
65
+ expiresAt: ts.expiresAt,
66
+ refreshTokenExpiresAt: ts.refreshTokenExpiresAt,
67
+ });
68
+ }
69
+ } catch {
70
+ // non-critical
71
+ }
72
+ }
73
+
74
+ // ── Tickets ────────────────────────────────────────────────────────────────
75
+
76
+ export async function fetchTickets({ context } = {}) {
77
+ const filters = { visibility: 0 };
78
+ if (context?.type === 'account' && context.id) filters.account = context.id;
79
+ if (context?.type === 'project' && context.id) filters.project = context.id;
80
+
81
+ const result = await client.api.listTickets({
82
+ fields: ['ID', 'ticketnum', 'name', 'status', 'priority', 'duedate', 'lastmodified'],
83
+ filters,
84
+ sort: ['-lastmodified'],
85
+ limit: 500,
86
+ });
87
+ await syncTokens();
88
+ return normalizeListResult(result).data;
89
+ }
90
+
91
+ export async function getTicket(id) {
92
+ const result = await client.api.getTicket({ ID: id, extdata: 1, tags: 1 });
93
+ await syncTokens();
94
+ return result;
95
+ }
96
+
97
+ export async function createTicket(data) {
98
+ const result = await client.api.createTicket(data);
99
+ await syncTokens();
100
+ return result;
101
+ }
102
+
103
+ export async function updateTicket(id, data) {
104
+ const result = await client.api.updateTicket({ ID: id, body: data });
105
+ await syncTokens();
106
+ return result;
107
+ }
108
+
109
+ export async function deleteTicket(id) {
110
+ await client.api.deleteTicket({ ID: id });
111
+ await syncTokens();
112
+ }
113
+
114
+ // ── Tasks ──────────────────────────────────────────────────────────────────
115
+
116
+ export async function fetchTasksForTicket(ticketId) {
117
+ const result = await client.api.listTasks({
118
+ fields: ['ID', 'tasknum', 'name', 'status', 'ticket', 'duedate', 'assigneduser'],
119
+ filters: { ticket: ticketId, visibility: 0 },
120
+ sort: ['+name'],
121
+ limit: 200,
122
+ });
123
+ return normalizeListResult(result).data;
124
+ }
125
+
126
+ export async function createTask(data) {
127
+ return client.api.createTask(data);
128
+ }
129
+
130
+ export async function updateTask(id, data) {
131
+ return client.api.updateTask({ ID: id, body: data });
132
+ }
133
+
134
+ export async function deleteTask(id) {
135
+ return client.api.deleteTask({ ID: id });
136
+ }
137
+
138
+ // ── Projects ───────────────────────────────────────────────────────────────
139
+
140
+ export async function fetchProjects() {
141
+ try {
142
+ const result = await client.api.listProjects({
143
+ fields: ['ID', 'name'],
144
+ filters: { visibility: 0 },
145
+ sort: ['+name'],
146
+ limit: 500,
147
+ });
148
+ return normalizeListResult(result).data;
149
+ } catch {
150
+ return [];
151
+ }
152
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Authentication helpers.
3
+ * Supports two modes:
4
+ * - Token mode: pre-obtained browser tokens provided via body attributes or console API
5
+ * - Session mode: browser cookies sent to ZeyOS (user already logged in)
6
+ */
7
+ import { client } from './api.js';
8
+ import { loadTokens, clearTokens } from './state.js';
9
+
10
+ // ── Public API ─────────────────────────────────────────────────────────────
11
+
12
+ /**
13
+ * Probe the ZeyOS instance for an active browser session.
14
+ * Calls GET {url}/oauth2/v1/userinfo with credentials:'include' to
15
+ * check whether the user is already logged in via a session cookie.
16
+ * Returns the user info object on success, null otherwise.
17
+ */
18
+ export async function trySessionAuth(url) {
19
+ try {
20
+ const endpoint = `${url.replace(/\/+$/, '')}/oauth2/v1/userinfo`;
21
+ const res = await fetch(endpoint, {
22
+ method: 'GET',
23
+ credentials: 'include',
24
+ headers: { 'Accept': 'application/json' },
25
+ });
26
+ if (!res.ok) return null;
27
+ const data = await res.json();
28
+ // A valid userinfo response must have a subject (user identifier)
29
+ return (data && (data.sub || data.ID)) ? data : null;
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Returns true if a valid (non-expired) access token exists in localStorage.
37
+ */
38
+ export function isAuthenticated() {
39
+ const tokens = loadTokens();
40
+ if (!tokens?.accessToken) return false;
41
+ if (!tokens.expiresAt) return true;
42
+ // Token still valid with 2-minute buffer (expiresAt is Unix seconds)
43
+ return Date.now() / 1000 < tokens.expiresAt - 120;
44
+ }
45
+
46
+ /**
47
+ * Clear tokens and revoke the access token if possible.
48
+ */
49
+ export async function logout() {
50
+ const tokens = loadTokens();
51
+ if (tokens?.accessToken && client) {
52
+ try {
53
+ await client.oauth2.revokeToken({ token: tokens.accessToken });
54
+ } catch {
55
+ // best-effort revocation
56
+ }
57
+ }
58
+ clearTokens();
59
+ }
@@ -0,0 +1,40 @@
1
+ export const STATUSES = [
2
+ { value: 0, key: 'NOTSTARTED', label: 'Not Started', headerBg: '#f1f5f9', headerText: '#475569', cardBorder: '#94a3b8' },
3
+ { value: 1, key: 'AWAITINGACCEPTANCE', label: 'Awaiting Acceptance', headerBg: '#fffbeb', headerText: '#92400e', cardBorder: '#f59e0b' },
4
+ { value: 2, key: 'ACCEPTED', label: 'Accepted', headerBg: '#f0fdf4', headerText: '#166534', cardBorder: '#22c55e' },
5
+ { value: 3, key: 'REJECTED', label: 'Rejected', headerBg: '#fef2f2', headerText: '#991b1b', cardBorder: '#ef4444' },
6
+ { value: 4, key: 'ACTIVE', label: 'Active', headerBg: '#eff6ff', headerText: '#1e40af', cardBorder: '#3b82f6' },
7
+ { value: 5, key: 'INACTIVE', label: 'Inactive', headerBg: '#f9fafb', headerText: '#4b5563', cardBorder: '#9ca3af' },
8
+ { value: 6, key: 'FEEDBACKREQUIRED', label: 'Feedback Required', headerBg: '#fff7ed', headerText: '#9a3412', cardBorder: '#f97316' },
9
+ { value: 7, key: 'TESTING', label: 'Testing', headerBg: '#faf5ff', headerText: '#6b21a8', cardBorder: '#a855f7' },
10
+ { value: 8, key: 'CANCELLED', label: 'Cancelled', headerBg: '#fff1f2', headerText: '#9f1239', cardBorder: '#fb7185' },
11
+ { value: 9, key: 'COMPLETED', label: 'Completed', headerBg: '#ecfdf5', headerText: '#065f46', cardBorder: '#10b981' },
12
+ { value: 10, key: 'FAILED', label: 'Failed', headerBg: '#fef2f2', headerText: '#7f1d1d', cardBorder: '#dc2626' },
13
+ { value: 11, key: 'BOOKED', label: 'Booked', headerBg: '#f0fdfa', headerText: '#134e4a', cardBorder: '#14b8a6' },
14
+ ];
15
+
16
+ export const STATUS_MAP = Object.fromEntries(STATUSES.map(s => [s.value, s]));
17
+
18
+ export const PRIORITIES = [
19
+ { value: 0, label: 'Lowest', symbol: '↓↓', color: '#94a3b8' },
20
+ { value: 1, label: 'Low', symbol: '↓', color: '#60a5fa' },
21
+ { value: 2, label: 'Medium', symbol: '→', color: '#4ade80' },
22
+ { value: 3, label: 'High', symbol: '↑', color: '#fb923c' },
23
+ { value: 4, label: 'Highest', symbol: '↑↑', color: '#f87171' },
24
+ ];
25
+
26
+ export const PRIORITY_MAP = Object.fromEntries(PRIORITIES.map(p => [p.value, p]));
27
+
28
+ export const TICKET_FIELDS = [
29
+ { key: 'name', label: 'Name' },
30
+ { key: 'ticketnum', label: 'Ticket #' },
31
+ { key: 'description', label: 'Description' },
32
+ { key: 'assigneduser', label: 'Assigned User' },
33
+ { key: 'duedate', label: 'Due Date' },
34
+ { key: 'priority', label: 'Priority' },
35
+ { key: 'status', label: 'Status' },
36
+ { key: 'account', label: 'Account' },
37
+ { key: 'project', label: 'Project' },
38
+ { key: 'creationdate', label: 'Created' },
39
+ { key: 'lastmodified', label: 'Last Modified' },
40
+ ];