@pillar-ai/sdk 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.
- package/README.md +129 -0
- package/dist/actions/definitions/analytics.d.ts +18 -0
- package/dist/actions/definitions/content.d.ts +40 -0
- package/dist/actions/definitions/index.d.ts +26 -0
- package/dist/actions/definitions/navigation.d.ts +65 -0
- package/dist/actions/definitions/settings.d.ts +162 -0
- package/dist/actions/definitions/sources.d.ts +44 -0
- package/dist/actions/definitions/support.d.ts +15 -0
- package/dist/actions/definitions/team.d.ts +120 -0
- package/dist/actions/index.d.ts +33 -0
- package/dist/actions/registry.d.ts +110 -0
- package/dist/actions/types.d.ts +388 -0
- package/dist/api/client.d.ts +186 -0
- package/dist/api/mcp-client.d.ts +226 -0
- package/dist/button/FloatingButton.d.ts +44 -0
- package/dist/cli/sync.d.ts +2 -0
- package/dist/components/Button/EdgeTrigger.d.ts +78 -0
- package/dist/components/Button/FloatingButton.d.ts +46 -0
- package/dist/components/Button/index.d.ts +5 -0
- package/dist/components/Cards/ConfirmActionCard.d.ts +23 -0
- package/dist/components/Cards/index.d.ts +6 -0
- package/dist/components/Panel/ChatInput.d.ts +5 -0
- package/dist/components/Panel/ContextTag.d.ts +18 -0
- package/dist/components/Panel/Header.d.ts +14 -0
- package/dist/components/Panel/Panel.d.ts +104 -0
- package/dist/components/Panel/PanelContent.d.ts +6 -0
- package/dist/components/Panel/TaskButton.d.ts +59 -0
- package/dist/components/Panel/UnifiedChatInput.d.ts +23 -0
- package/dist/components/Panel/WorkflowChecklist.d.ts +9 -0
- package/dist/components/Panel/index.d.ts +13 -0
- package/dist/components/Panel/styles.d.ts +20 -0
- package/dist/components/Plan/InlinePlanView.d.ts +24 -0
- package/dist/components/Plan/PlanStepItem.d.ts +18 -0
- package/dist/components/Plan/PlanView.d.ts +10 -0
- package/dist/components/Plan/index.d.ts +8 -0
- package/dist/components/TextSelection/TextSelectionManager.d.ts +34 -0
- package/dist/components/TextSelection/TextSelectionPopover.d.ts +14 -0
- package/dist/components/TextSelection/index.d.ts +6 -0
- package/dist/components/TextSelection/styles.d.ts +5 -0
- package/dist/components/Tooltips/Tooltip.d.ts +46 -0
- package/dist/components/Tooltips/TooltipManager.d.ts +41 -0
- package/dist/components/Tooltips/index.d.ts +6 -0
- package/dist/components/Tooltips/styles.d.ts +5 -0
- package/dist/components/Views/ArticleChatView.d.ts +9 -0
- package/dist/components/Views/ArticleView.d.ts +10 -0
- package/dist/components/Views/CategoryView.d.ts +11 -0
- package/dist/components/Views/ChatView.d.ts +5 -0
- package/dist/components/Views/DeveloperView.d.ts +6 -0
- package/dist/components/Views/HomeView.d.ts +5 -0
- package/dist/components/Views/SearchView.d.ts +10 -0
- package/dist/components/Views/index.d.ts +5 -0
- package/dist/components/context.d.ts +21 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/components/shared/ArticleCard.d.ts +17 -0
- package/dist/components/shared/CategoryCard.d.ts +17 -0
- package/dist/components/shared/Empty.d.ts +11 -0
- package/dist/components/shared/Loading.d.ts +6 -0
- package/dist/components/shared/MessageInputArea.d.ts +19 -0
- package/dist/components/shared/QuestionChip.d.ts +14 -0
- package/dist/components/shared/index.d.ts +7 -0
- package/dist/content/extensions/AccordionNode.d.ts +10 -0
- package/dist/content/extensions/CalloutNode.d.ts +11 -0
- package/dist/content/extensions/index.d.ts +5 -0
- package/dist/content/index.d.ts +5 -0
- package/dist/content/renderer.d.ts +24 -0
- package/dist/core/Pillar.d.ts +454 -0
- package/dist/core/config.d.ts +253 -0
- package/dist/core/context.d.ts +71 -0
- package/dist/core/events.d.ts +228 -0
- package/dist/core/plan-executor.d.ts +101 -0
- package/dist/core/plan.d.ts +155 -0
- package/dist/core/workflow.d.ts +89 -0
- package/dist/index.d.ts +32 -0
- package/dist/panel/Panel.d.ts +53 -0
- package/dist/panel/PanelUI.d.ts +43 -0
- package/dist/panel/components/ArticleCard.d.ts +10 -0
- package/dist/panel/components/CategoryCard.d.ts +10 -0
- package/dist/panel/components/ChatInput.d.ts +36 -0
- package/dist/panel/components/Header.d.ts +16 -0
- package/dist/panel/components/SearchInput.d.ts +11 -0
- package/dist/panel/styles.d.ts +5 -0
- package/dist/panel/views/ArticleView.d.ts +21 -0
- package/dist/panel/views/CategoryView.d.ts +20 -0
- package/dist/panel/views/ChatView.d.ts +30 -0
- package/dist/panel/views/HomeView.d.ts +18 -0
- package/dist/panel/views/SearchView.d.ts +22 -0
- package/dist/pillar.esm.js +10338 -0
- package/dist/pillar.esm.js.map +1 -0
- package/dist/pillar.js +10362 -0
- package/dist/pillar.js.map +1 -0
- package/dist/pillar.min.js +2 -0
- package/dist/pillar.min.js.map +1 -0
- package/dist/store/chat.d.ts +85 -0
- package/dist/store/context.d.ts +46 -0
- package/dist/store/developer.d.ts +19 -0
- package/dist/store/index.d.ts +10 -0
- package/dist/store/panel.d.ts +43 -0
- package/dist/store/plan-persistence.d.ts +47 -0
- package/dist/store/plan.d.ts +45 -0
- package/dist/store/router.d.ts +18 -0
- package/dist/store/tooltips.d.ts +21 -0
- package/dist/store/workflow.d.ts +48 -0
- package/dist/tooltips/Tooltip.d.ts +63 -0
- package/dist/tooltips/TooltipManager.d.ts +42 -0
- package/dist/tooltips/styles.d.ts +5 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/user-context.d.ts +29 -0
- package/dist/utils/dom.d.ts +46 -0
- package/dist/utils/markdown.d.ts +9 -0
- package/dist/utils/positioning.d.ts +52 -0
- package/dist/utils/urlParams.d.ts +27 -0
- package/package.json +86 -0
- package/src/actions/types.ts +468 -0
- package/src/cli/sync.ts +477 -0
package/src/cli/sync.ts
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Pillar Action Sync CLI
|
|
4
|
+
*
|
|
5
|
+
* Syncs action definitions to the Pillar backend.
|
|
6
|
+
* Run this in your CI/CD pipeline after building your app.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx pillar-sync --actions ./path/to/actions.ts
|
|
10
|
+
*
|
|
11
|
+
* Environment (required):
|
|
12
|
+
* PILLAR_SLUG - Your help center slug (e.g., "acme-corp")
|
|
13
|
+
* PILLAR_SECRET - Secret token for authentication
|
|
14
|
+
*
|
|
15
|
+
* Environment (optional):
|
|
16
|
+
* PILLAR_API_URL - Pillar API URL (defaults to https://api.trypillar.com)
|
|
17
|
+
* PILLAR_PLATFORM - Platform identifier (web, ios, android, desktop)
|
|
18
|
+
* PILLAR_VERSION - App version (semver or git SHA)
|
|
19
|
+
* GIT_SHA - Git commit SHA (optional, for traceability)
|
|
20
|
+
*/
|
|
21
|
+
import * as fs from 'fs';
|
|
22
|
+
import * as path from 'path';
|
|
23
|
+
import { execSync } from 'child_process';
|
|
24
|
+
import { pathToFileURL } from 'url';
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Types (inline to make CLI self-contained)
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
type ActionType =
|
|
31
|
+
| 'navigate'
|
|
32
|
+
| 'open_modal'
|
|
33
|
+
| 'fill_form'
|
|
34
|
+
| 'trigger_action'
|
|
35
|
+
| 'copy_text'
|
|
36
|
+
| 'external_link'
|
|
37
|
+
| 'start_tutorial'
|
|
38
|
+
| 'inline_ui';
|
|
39
|
+
|
|
40
|
+
type Platform = 'web' | 'ios' | 'android' | 'desktop';
|
|
41
|
+
|
|
42
|
+
interface ActionDataSchema {
|
|
43
|
+
type: 'object';
|
|
44
|
+
properties: Record<
|
|
45
|
+
string,
|
|
46
|
+
{
|
|
47
|
+
type: 'string' | 'number' | 'boolean' | 'array' | 'object';
|
|
48
|
+
description?: string;
|
|
49
|
+
enum?: string[];
|
|
50
|
+
default?: unknown;
|
|
51
|
+
}
|
|
52
|
+
>;
|
|
53
|
+
required?: string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface SyncActionDefinition {
|
|
57
|
+
description: string;
|
|
58
|
+
examples?: string[];
|
|
59
|
+
type: ActionType;
|
|
60
|
+
path?: string;
|
|
61
|
+
externalUrl?: string;
|
|
62
|
+
dataSchema?: ActionDataSchema;
|
|
63
|
+
defaultData?: Record<string, unknown>;
|
|
64
|
+
requiredContext?: Record<string, unknown>;
|
|
65
|
+
autoRun?: boolean;
|
|
66
|
+
autoComplete?: boolean;
|
|
67
|
+
returns?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type SyncActionDefinitions = Record<string, SyncActionDefinition>;
|
|
71
|
+
|
|
72
|
+
interface ActionManifestEntry {
|
|
73
|
+
name: string;
|
|
74
|
+
description: string;
|
|
75
|
+
examples?: string[];
|
|
76
|
+
type: ActionType;
|
|
77
|
+
path?: string;
|
|
78
|
+
external_url?: string;
|
|
79
|
+
auto_run?: boolean;
|
|
80
|
+
auto_complete?: boolean;
|
|
81
|
+
returns_data?: boolean;
|
|
82
|
+
data_schema?: ActionDataSchema;
|
|
83
|
+
default_data?: Record<string, unknown>;
|
|
84
|
+
required_context?: Record<string, unknown>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface ActionManifest {
|
|
88
|
+
platform: Platform;
|
|
89
|
+
version: string;
|
|
90
|
+
gitSha?: string;
|
|
91
|
+
generatedAt: string;
|
|
92
|
+
actions: ActionManifestEntry[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
interface SyncResponse {
|
|
96
|
+
status: 'created' | 'unchanged' | 'accepted';
|
|
97
|
+
deployment_id?: string;
|
|
98
|
+
version: string;
|
|
99
|
+
actions_count?: number;
|
|
100
|
+
created?: number;
|
|
101
|
+
updated?: number;
|
|
102
|
+
deleted?: number;
|
|
103
|
+
job_id?: string;
|
|
104
|
+
status_url?: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface StatusResponse {
|
|
108
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
109
|
+
is_complete: boolean;
|
|
110
|
+
progress: {
|
|
111
|
+
total: number;
|
|
112
|
+
processed: number;
|
|
113
|
+
created: number;
|
|
114
|
+
updated: number;
|
|
115
|
+
deleted: number;
|
|
116
|
+
};
|
|
117
|
+
deployment_id?: string;
|
|
118
|
+
error?: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ============================================================================
|
|
122
|
+
// CLI Implementation
|
|
123
|
+
// ============================================================================
|
|
124
|
+
|
|
125
|
+
// Default API URL for production
|
|
126
|
+
const DEFAULT_API_URL = 'https://api.trypillar.com';
|
|
127
|
+
const LOCAL_API_URL = 'http://localhost:8003';
|
|
128
|
+
|
|
129
|
+
function parseArgs(args: string[]): Record<string, string | boolean> {
|
|
130
|
+
const result: Record<string, string | boolean> = {};
|
|
131
|
+
|
|
132
|
+
for (let i = 0; i < args.length; i++) {
|
|
133
|
+
const arg = args[i];
|
|
134
|
+
if (arg.startsWith('--')) {
|
|
135
|
+
const key = arg.slice(2);
|
|
136
|
+
const nextArg = args[i + 1];
|
|
137
|
+
|
|
138
|
+
if (nextArg && !nextArg.startsWith('--')) {
|
|
139
|
+
result[key] = nextArg;
|
|
140
|
+
i++;
|
|
141
|
+
} else {
|
|
142
|
+
result[key] = true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function printUsage(): void {
|
|
151
|
+
console.log(`
|
|
152
|
+
Pillar Action Sync CLI
|
|
153
|
+
|
|
154
|
+
Usage:
|
|
155
|
+
npx pillar-sync --actions <path> [--local]
|
|
156
|
+
|
|
157
|
+
Arguments:
|
|
158
|
+
--actions <path> Path to your actions definition file (required)
|
|
159
|
+
Supports .ts, .js, .mjs files
|
|
160
|
+
--local Use localhost:8003 as the API URL (for local development)
|
|
161
|
+
--help Show this help message
|
|
162
|
+
|
|
163
|
+
Environment Variables:
|
|
164
|
+
PILLAR_SLUG Your help center slug (required)
|
|
165
|
+
PILLAR_SECRET Secret token for authentication (required)
|
|
166
|
+
PILLAR_API_URL API URL (default: https://api.trypillar.com)
|
|
167
|
+
PILLAR_PLATFORM Platform: web, ios, android, desktop (default: web)
|
|
168
|
+
PILLAR_VERSION App version (default: from package.json)
|
|
169
|
+
GIT_SHA Git commit SHA for traceability
|
|
170
|
+
|
|
171
|
+
Examples:
|
|
172
|
+
# Production
|
|
173
|
+
PILLAR_SLUG=my-app PILLAR_SECRET=xxx npx pillar-sync --actions ./lib/actions.ts
|
|
174
|
+
|
|
175
|
+
# Local development
|
|
176
|
+
PILLAR_SLUG=my-app PILLAR_SECRET=xxx npx pillar-sync --actions ./lib/actions.ts --local
|
|
177
|
+
`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function loadActions(actionsPath: string): Promise<SyncActionDefinitions> {
|
|
181
|
+
const absolutePath = path.resolve(process.cwd(), actionsPath);
|
|
182
|
+
|
|
183
|
+
if (!fs.existsSync(absolutePath)) {
|
|
184
|
+
throw new Error(`Actions file not found: ${absolutePath}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Convert to file URL for ESM compatibility
|
|
188
|
+
const fileUrl = pathToFileURL(absolutePath).href;
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const module = await import(fileUrl);
|
|
192
|
+
|
|
193
|
+
// Support default export or named 'actions' export
|
|
194
|
+
const actions = module.default || module.actions;
|
|
195
|
+
|
|
196
|
+
if (!actions || typeof actions !== 'object') {
|
|
197
|
+
throw new Error(
|
|
198
|
+
'Actions file must export an actions object as default or named export "actions"'
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return actions;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (error instanceof Error && error.message.includes('Unknown file extension')) {
|
|
205
|
+
console.error('[pillar-sync] TypeScript files require tsx.');
|
|
206
|
+
console.error('[pillar-sync] Make sure tsx is installed: npm install -D tsx');
|
|
207
|
+
console.error('[pillar-sync] Then run: npx pillar-sync --actions ./actions.ts');
|
|
208
|
+
}
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function buildManifest(
|
|
214
|
+
actions: SyncActionDefinitions,
|
|
215
|
+
platform: Platform,
|
|
216
|
+
version: string,
|
|
217
|
+
gitSha?: string
|
|
218
|
+
): ActionManifest {
|
|
219
|
+
const entries: ActionManifestEntry[] = [];
|
|
220
|
+
|
|
221
|
+
for (const [name, definition] of Object.entries(actions)) {
|
|
222
|
+
const entry: ActionManifestEntry = {
|
|
223
|
+
name,
|
|
224
|
+
description: definition.description,
|
|
225
|
+
type: definition.type,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// Only include optional fields if they have values
|
|
229
|
+
if (definition.examples?.length) entry.examples = definition.examples;
|
|
230
|
+
if (definition.path) entry.path = definition.path;
|
|
231
|
+
if (definition.externalUrl) entry.external_url = definition.externalUrl;
|
|
232
|
+
if (definition.autoRun) entry.auto_run = definition.autoRun;
|
|
233
|
+
if (definition.autoComplete) entry.auto_complete = definition.autoComplete;
|
|
234
|
+
if (definition.returns) entry.returns_data = definition.returns;
|
|
235
|
+
if (definition.dataSchema) entry.data_schema = definition.dataSchema;
|
|
236
|
+
if (definition.defaultData) entry.default_data = definition.defaultData;
|
|
237
|
+
if (definition.requiredContext) entry.required_context = definition.requiredContext;
|
|
238
|
+
|
|
239
|
+
entries.push(entry);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
platform,
|
|
244
|
+
version,
|
|
245
|
+
gitSha,
|
|
246
|
+
generatedAt: new Date().toISOString(),
|
|
247
|
+
actions: entries,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function pollStatus(
|
|
252
|
+
statusUrl: string,
|
|
253
|
+
secret: string,
|
|
254
|
+
maxWaitSeconds: number = 300
|
|
255
|
+
): Promise<void> {
|
|
256
|
+
const startTime = Date.now();
|
|
257
|
+
let lastProgress = { processed: 0, total: 0 };
|
|
258
|
+
|
|
259
|
+
while (true) {
|
|
260
|
+
try {
|
|
261
|
+
const response = await fetch(statusUrl, {
|
|
262
|
+
headers: {
|
|
263
|
+
'X-Pillar-Secret': secret,
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
if (!response.ok) {
|
|
268
|
+
throw new Error(`Status check failed: ${response.status} ${response.statusText}`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const status: StatusResponse = await response.json();
|
|
272
|
+
|
|
273
|
+
// Show progress updates
|
|
274
|
+
if (
|
|
275
|
+
status.progress &&
|
|
276
|
+
(status.progress.processed !== lastProgress.processed ||
|
|
277
|
+
status.progress.total !== lastProgress.total)
|
|
278
|
+
) {
|
|
279
|
+
const { processed, total, created, updated, deleted } = status.progress;
|
|
280
|
+
const percent = total > 0 ? Math.round((processed / total) * 100) : 0;
|
|
281
|
+
console.log(
|
|
282
|
+
`[pillar-sync] Progress: ${processed}/${total} (${percent}%) - ` +
|
|
283
|
+
`Created: ${created}, Updated: ${updated}, Deleted: ${deleted}`
|
|
284
|
+
);
|
|
285
|
+
lastProgress = { processed, total };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Check completion
|
|
289
|
+
if (status.status === 'completed' && status.is_complete) {
|
|
290
|
+
console.log(`[pillar-sync] ✓ Sync completed successfully`);
|
|
291
|
+
if (status.deployment_id) {
|
|
292
|
+
console.log(`[pillar-sync] Deployment: ${status.deployment_id}`);
|
|
293
|
+
}
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (status.status === 'failed') {
|
|
298
|
+
throw new Error(status.error || 'Sync job failed');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Check timeout
|
|
302
|
+
const elapsed = (Date.now() - startTime) / 1000;
|
|
303
|
+
if (elapsed > maxWaitSeconds) {
|
|
304
|
+
throw new Error(`Timeout after ${maxWaitSeconds} seconds`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Wait before next poll
|
|
308
|
+
await sleep(2000);
|
|
309
|
+
} catch (error) {
|
|
310
|
+
if (error instanceof Error && error.message.includes('Timeout')) {
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
console.error(`[pillar-sync] Poll error: ${error}`);
|
|
314
|
+
await sleep(2000);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function sleep(ms: number): Promise<void> {
|
|
320
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function getPackageVersion(): string {
|
|
324
|
+
try {
|
|
325
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
326
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
327
|
+
return pkg.version || '0.0.0';
|
|
328
|
+
} catch {
|
|
329
|
+
return '0.0.0';
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function getGitSha(): string | undefined {
|
|
334
|
+
try {
|
|
335
|
+
return execSync('git rev-parse HEAD', { encoding: 'utf-8' }).trim().slice(0, 7);
|
|
336
|
+
} catch {
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async function main(): Promise<void> {
|
|
342
|
+
const args = parseArgs(process.argv.slice(2));
|
|
343
|
+
|
|
344
|
+
// Show help
|
|
345
|
+
if (args.help) {
|
|
346
|
+
printUsage();
|
|
347
|
+
process.exit(0);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Validate --actions argument
|
|
351
|
+
const actionsPath = args.actions as string | undefined;
|
|
352
|
+
if (!actionsPath) {
|
|
353
|
+
console.error('[pillar-sync] Missing required --actions argument');
|
|
354
|
+
console.error('');
|
|
355
|
+
printUsage();
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Get configuration from environment
|
|
360
|
+
const isLocal = args.local === true;
|
|
361
|
+
const apiUrl = isLocal ? LOCAL_API_URL : (process.env.PILLAR_API_URL || DEFAULT_API_URL);
|
|
362
|
+
const slug = process.env.PILLAR_SLUG;
|
|
363
|
+
const secret = process.env.PILLAR_SECRET;
|
|
364
|
+
|
|
365
|
+
if (isLocal) {
|
|
366
|
+
console.log(`[pillar-sync] Using local API: ${LOCAL_API_URL}`);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (!slug || !secret) {
|
|
370
|
+
console.error('[pillar-sync] Missing required environment variables:');
|
|
371
|
+
if (!slug) console.error(' - PILLAR_SLUG');
|
|
372
|
+
if (!secret) console.error(' - PILLAR_SECRET');
|
|
373
|
+
console.error('');
|
|
374
|
+
console.error('Get these from the Pillar admin: Actions → Configure Sync');
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Load actions from user's file
|
|
379
|
+
console.log(`[pillar-sync] Loading actions from: ${actionsPath}`);
|
|
380
|
+
let actions: SyncActionDefinitions;
|
|
381
|
+
try {
|
|
382
|
+
actions = await loadActions(actionsPath);
|
|
383
|
+
} catch (error) {
|
|
384
|
+
console.error(`[pillar-sync] Failed to load actions:`, error);
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const actionCount = Object.keys(actions).length;
|
|
389
|
+
console.log(`[pillar-sync] Found ${actionCount} actions`);
|
|
390
|
+
|
|
391
|
+
if (actionCount === 0) {
|
|
392
|
+
console.warn('[pillar-sync] No actions found. Nothing to sync.');
|
|
393
|
+
process.exit(0);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Build configuration
|
|
397
|
+
const platform = (process.env.PILLAR_PLATFORM || 'web') as Platform;
|
|
398
|
+
const version = process.env.PILLAR_VERSION || getPackageVersion();
|
|
399
|
+
const gitSha = process.env.GIT_SHA || getGitSha();
|
|
400
|
+
|
|
401
|
+
console.log(`[pillar-sync] Platform: ${platform}`);
|
|
402
|
+
console.log(`[pillar-sync] Version: ${version}`);
|
|
403
|
+
console.log(`[pillar-sync] Git SHA: ${gitSha || 'not available'}`);
|
|
404
|
+
|
|
405
|
+
// Generate manifest
|
|
406
|
+
const manifest = buildManifest(actions, platform, version, gitSha);
|
|
407
|
+
|
|
408
|
+
// Optionally write manifest to disk for debugging
|
|
409
|
+
if (process.env.PILLAR_DEBUG) {
|
|
410
|
+
const manifestPath = path.join(process.cwd(), 'actions-manifest.json');
|
|
411
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
412
|
+
console.log(`[pillar-sync] Wrote manifest to ${manifestPath}`);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Sync to backend
|
|
416
|
+
console.log(`[pillar-sync] Help Center: ${slug}`);
|
|
417
|
+
|
|
418
|
+
const requestBody = {
|
|
419
|
+
platform: manifest.platform,
|
|
420
|
+
version: manifest.version,
|
|
421
|
+
git_sha: gitSha,
|
|
422
|
+
actions: manifest.actions,
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const syncUrl = `${apiUrl}/api/admin/configs/${slug}/actions/sync/?async=true`;
|
|
426
|
+
console.log(`[pillar-sync] POST ${syncUrl}`);
|
|
427
|
+
|
|
428
|
+
try {
|
|
429
|
+
const response = await fetch(syncUrl, {
|
|
430
|
+
method: 'POST',
|
|
431
|
+
headers: {
|
|
432
|
+
'Content-Type': 'application/json',
|
|
433
|
+
'X-Pillar-Secret': secret,
|
|
434
|
+
},
|
|
435
|
+
body: JSON.stringify(requestBody),
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
if (!response.ok) {
|
|
439
|
+
const errorText = await response.text();
|
|
440
|
+
console.error(`[pillar-sync] Sync failed: ${response.status} ${response.statusText}`);
|
|
441
|
+
console.error(`[pillar-sync] Response: ${errorText}`);
|
|
442
|
+
process.exit(1);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const result: SyncResponse = await response.json();
|
|
446
|
+
|
|
447
|
+
if (result.status === 'unchanged') {
|
|
448
|
+
console.log(`[pillar-sync] ✓ Manifest unchanged (deployment ${result.deployment_id})`);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (result.status === 'accepted' && result.job_id && result.status_url) {
|
|
453
|
+
console.log(`[pillar-sync] ✓ Job accepted (job ${result.job_id})`);
|
|
454
|
+
console.log(`[pillar-sync] Polling for completion...`);
|
|
455
|
+
|
|
456
|
+
const statusUrl = result.status_url.startsWith('http')
|
|
457
|
+
? result.status_url
|
|
458
|
+
: `${apiUrl}${result.status_url}`;
|
|
459
|
+
|
|
460
|
+
await pollStatus(statusUrl, secret);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (result.status === 'created') {
|
|
465
|
+
console.log(`[pillar-sync] ✓ Created deployment ${result.deployment_id}`);
|
|
466
|
+
console.log(`[pillar-sync] Actions: ${result.actions_count}`);
|
|
467
|
+
console.log(
|
|
468
|
+
`[pillar-sync] Created: ${result.created}, Updated: ${result.updated}, Deleted: ${result.deleted || 0}`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
} catch (error) {
|
|
472
|
+
console.error('[pillar-sync] Sync failed:', error);
|
|
473
|
+
process.exit(1);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
main();
|