@parall/cli 1.35.0 → 1.36.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/dist/commands/external-triggers.d.ts +19 -0
- package/dist/commands/external-triggers.d.ts.map +1 -0
- package/dist/commands/external-triggers.js +488 -0
- package/dist/commands/tasks.d.ts.map +1 -1
- package/dist/commands/tasks.js +15 -0
- package/dist/commands/wiki.d.ts.map +1 -1
- package/dist/commands/wiki.js +90 -0
- package/dist/index.js +2 -0
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/client.js +6 -0
- package/dist/lib/wiki-files.d.ts +76 -0
- package/dist/lib/wiki-files.d.ts.map +1 -0
- package/dist/lib/wiki-files.js +169 -0
- package/dist/lib/wiki-tools.d.ts +3 -2
- package/dist/lib/wiki-tools.d.ts.map +1 -1
- package/dist/lib/wiki-tools.js +171 -2
- package/dist/lib/wiki.d.ts +12 -0
- package/dist/lib/wiki.d.ts.map +1 -1
- package/dist/lib/wiki.js +26 -13
- package/package.json +3 -3
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ExternalConnection } from '@parall/sdk';
|
|
2
|
+
import type { Command } from 'commander';
|
|
3
|
+
export declare const EXTERNAL_TRIGGERS_FLAG = "external-triggers";
|
|
4
|
+
type ExternalTriggerFeatureFlagClient = {
|
|
5
|
+
getFeatureFlags(orgId: string): Promise<{
|
|
6
|
+
flags: Record<string, boolean | string | number>;
|
|
7
|
+
}>;
|
|
8
|
+
};
|
|
9
|
+
type IngressTokenOutputOptions = {
|
|
10
|
+
tokenFile?: string;
|
|
11
|
+
showToken?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function isFeatureFlagEnabled(value: boolean | string | number | undefined): boolean;
|
|
14
|
+
export declare function requireExternalTriggersEnabled(client: ExternalTriggerFeatureFlagClient, orgId: string): Promise<void>;
|
|
15
|
+
export declare function validateIngressTokenOutputOptions(opts: IngressTokenOutputOptions): void;
|
|
16
|
+
export declare function connectionTokenOutput(connection: ExternalConnection, opts: IngressTokenOutputOptions): Promise<ExternalConnection | Record<string, unknown>>;
|
|
17
|
+
export declare function registerExternalTriggerCommands(program: Command): void;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=external-triggers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-triggers.d.ts","sourceRoot":"","sources":["../../src/commands/external-triggers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,kBAAkB,EAInB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMzC,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAE1D,KAAK,gCAAgC,GAAG;IACtC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC,CAAA;KAAE,CAAC,CAAC;CAC/F,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAsDF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAM1F;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,gCAAgC,EACxC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAKf;AAQD,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,yBAAyB,GAAG,IAAI,CAKvF;AAED,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,kBAAkB,EAC9B,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAqBvD;AAkBD,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,OAAO,QAqZ/D"}
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { chmod, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { resolveCredentials } from '../lib/client.js';
|
|
3
|
+
import { printError, printJson, printRefHint, stripPrllScheme } from '../lib/output.js';
|
|
4
|
+
const connectionStatuses = ['active', 'disabled'];
|
|
5
|
+
const triggerStatuses = ['active', 'paused'];
|
|
6
|
+
export const EXTERNAL_TRIGGERS_FLAG = 'external-triggers';
|
|
7
|
+
function stripUndefined(obj) {
|
|
8
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
|
|
9
|
+
}
|
|
10
|
+
function parseIds(value, flag) {
|
|
11
|
+
const ids = value
|
|
12
|
+
.split(',')
|
|
13
|
+
.map((s) => stripPrllScheme(s.trim()))
|
|
14
|
+
.filter(Boolean);
|
|
15
|
+
if (ids.length === 0) {
|
|
16
|
+
throw new Error(`${flag} must include at least one ID.`);
|
|
17
|
+
}
|
|
18
|
+
return ids;
|
|
19
|
+
}
|
|
20
|
+
function parsePositiveInt(value, flag) {
|
|
21
|
+
if (value === undefined)
|
|
22
|
+
return undefined;
|
|
23
|
+
const normalized = value.trim();
|
|
24
|
+
const parsed = Number(normalized);
|
|
25
|
+
if (normalized === '' || !Number.isInteger(parsed) || parsed <= 0) {
|
|
26
|
+
throw new Error(`${flag} must be a positive integer.`);
|
|
27
|
+
}
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
function parseEnum(value, allowed, flag) {
|
|
31
|
+
if (value === undefined)
|
|
32
|
+
return undefined;
|
|
33
|
+
if (allowed.includes(value))
|
|
34
|
+
return value;
|
|
35
|
+
throw new Error(`${flag} must be one of: ${allowed.join(', ')}.`);
|
|
36
|
+
}
|
|
37
|
+
function parseJsonObject(value, flag) {
|
|
38
|
+
if (value === undefined)
|
|
39
|
+
return undefined;
|
|
40
|
+
let parsed;
|
|
41
|
+
try {
|
|
42
|
+
parsed = JSON.parse(value);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
throw new Error(`${flag} must be a valid JSON object.`);
|
|
46
|
+
}
|
|
47
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
48
|
+
throw new Error(`${flag} must be a JSON object.`);
|
|
49
|
+
}
|
|
50
|
+
return parsed;
|
|
51
|
+
}
|
|
52
|
+
export function isFeatureFlagEnabled(value) {
|
|
53
|
+
return (value === true ||
|
|
54
|
+
(typeof value === 'string' && value !== '') ||
|
|
55
|
+
(typeof value === 'number' && value !== 0));
|
|
56
|
+
}
|
|
57
|
+
export async function requireExternalTriggersEnabled(client, orgId) {
|
|
58
|
+
const res = await client.getFeatureFlags(orgId);
|
|
59
|
+
if (!isFeatureFlagEnabled(res.flags[EXTERNAL_TRIGGERS_FLAG])) {
|
|
60
|
+
throw new Error('External Triggers are disabled for this organization.');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function resolveExternalTriggerCommandContext() {
|
|
64
|
+
const ctx = resolveCredentials();
|
|
65
|
+
await requireExternalTriggersEnabled(ctx.client, ctx.orgId);
|
|
66
|
+
return ctx;
|
|
67
|
+
}
|
|
68
|
+
export function validateIngressTokenOutputOptions(opts) {
|
|
69
|
+
const count = [opts.tokenFile, opts.showToken].filter(Boolean).length;
|
|
70
|
+
if (count !== 1) {
|
|
71
|
+
throw new Error('Choose exactly one token output mode: --token-file <path> or --show-token.');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export async function connectionTokenOutput(connection, opts) {
|
|
75
|
+
validateIngressTokenOutputOptions(opts);
|
|
76
|
+
if (!connection.ingress_token) {
|
|
77
|
+
throw new Error('Server did not return an ingress token.');
|
|
78
|
+
}
|
|
79
|
+
if (opts.showToken)
|
|
80
|
+
return connection;
|
|
81
|
+
if (!opts.tokenFile) {
|
|
82
|
+
throw new Error('Choose exactly one token output mode: --token-file <path> or --show-token.');
|
|
83
|
+
}
|
|
84
|
+
await writeFile(opts.tokenFile, connection.ingress_token, { mode: 0o600 });
|
|
85
|
+
await chmod(opts.tokenFile, 0o600);
|
|
86
|
+
const redacted = { ...connection };
|
|
87
|
+
delete redacted.ingress_token;
|
|
88
|
+
delete redacted.ingress_url;
|
|
89
|
+
return {
|
|
90
|
+
...redacted,
|
|
91
|
+
ingress_token_redacted: true,
|
|
92
|
+
ingress_url_redacted: true,
|
|
93
|
+
ingress_token_file: opts.tokenFile,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
async function resolveTemplate(opts) {
|
|
97
|
+
if (opts.template !== undefined && opts.templateFile !== undefined) {
|
|
98
|
+
throw new Error('Do not pass --template together with --template-file.');
|
|
99
|
+
}
|
|
100
|
+
if (opts.templateFile !== undefined) {
|
|
101
|
+
return readFile(opts.templateFile, 'utf8');
|
|
102
|
+
}
|
|
103
|
+
if (opts.template !== undefined)
|
|
104
|
+
return opts.template;
|
|
105
|
+
if (opts.required)
|
|
106
|
+
throw new Error('Provide --template or --template-file.');
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
export function registerExternalTriggerCommands(program) {
|
|
110
|
+
const externalTriggers = program
|
|
111
|
+
.command('external-triggers')
|
|
112
|
+
.description('Manage External Triggers (incoming platform triggers for agents)');
|
|
113
|
+
externalTriggers
|
|
114
|
+
.command('connections')
|
|
115
|
+
.description('List External Trigger Connections in the organization')
|
|
116
|
+
.option('--limit <n>', 'Maximum number of connections to return', '50')
|
|
117
|
+
.option('--cursor <cursor>', 'Pagination cursor')
|
|
118
|
+
.action(async (opts) => {
|
|
119
|
+
try {
|
|
120
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
121
|
+
const result = await client.listExternalConnections(orgId, stripUndefined({
|
|
122
|
+
limit: parsePositiveInt(opts.limit, '--limit'),
|
|
123
|
+
cursor: opts.cursor,
|
|
124
|
+
}));
|
|
125
|
+
printJson(result);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
printError(err);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
externalTriggers
|
|
132
|
+
.command('create-connection')
|
|
133
|
+
.description('Create an External Trigger Connection and return its incoming endpoint')
|
|
134
|
+
.requiredOption('--name <name>', 'Connection display name')
|
|
135
|
+
.option('--token-file <path>', 'Write the one-time token to a file with mode 0600')
|
|
136
|
+
.option('--show-token', 'Print the one-time token to stdout')
|
|
137
|
+
.action(async (opts) => {
|
|
138
|
+
try {
|
|
139
|
+
validateIngressTokenOutputOptions(opts);
|
|
140
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
141
|
+
const result = await client.createExternalConnection(orgId, {
|
|
142
|
+
display_name: opts.name,
|
|
143
|
+
});
|
|
144
|
+
printJson(await connectionTokenOutput(result, opts));
|
|
145
|
+
printRefHint(result.id);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
printError(err);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
externalTriggers
|
|
152
|
+
.command('connection')
|
|
153
|
+
.description('Get an External Trigger Connection by ID')
|
|
154
|
+
.argument('<connectionId>', 'External Trigger Connection ID')
|
|
155
|
+
.action(async (connectionId) => {
|
|
156
|
+
try {
|
|
157
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
158
|
+
const result = await client.getExternalConnection(orgId, stripPrllScheme(connectionId));
|
|
159
|
+
printJson(result);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
printError(err);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
externalTriggers
|
|
166
|
+
.command('update-connection')
|
|
167
|
+
.description('Update an External Trigger Connection')
|
|
168
|
+
.argument('<connectionId>', 'External Trigger Connection ID')
|
|
169
|
+
.option('--name <name>', 'Connection display name')
|
|
170
|
+
.option('--status <status>', 'active | disabled')
|
|
171
|
+
.action(async (connectionId, opts) => {
|
|
172
|
+
try {
|
|
173
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
174
|
+
const patch = stripUndefined({
|
|
175
|
+
display_name: opts.name,
|
|
176
|
+
status: parseEnum(opts.status, connectionStatuses, '--status'),
|
|
177
|
+
});
|
|
178
|
+
if (Object.keys(patch).length === 0) {
|
|
179
|
+
printError(new Error('No fields to update. Provide at least one option.'));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const result = await client.updateExternalConnection(orgId, stripPrllScheme(connectionId), patch);
|
|
183
|
+
printJson(result);
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
printError(err);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
externalTriggers
|
|
190
|
+
.command('regenerate-connection-token')
|
|
191
|
+
.description('Regenerate a connection ingress token; old webhook URLs stop working')
|
|
192
|
+
.argument('<connectionId>', 'External Trigger Connection ID')
|
|
193
|
+
.option('--token-file <path>', 'Write the one-time token to a file with mode 0600')
|
|
194
|
+
.option('--show-token', 'Print the one-time token to stdout')
|
|
195
|
+
.action(async (connectionId, opts) => {
|
|
196
|
+
try {
|
|
197
|
+
validateIngressTokenOutputOptions(opts);
|
|
198
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
199
|
+
const result = await client.regenerateExternalConnectionIngressToken(orgId, stripPrllScheme(connectionId));
|
|
200
|
+
printJson(await connectionTokenOutput(result, opts));
|
|
201
|
+
printRefHint(result.id, 'Regenerated');
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
printError(err);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
externalTriggers
|
|
208
|
+
.command('delete-connection')
|
|
209
|
+
.description('Archive an External Trigger Connection')
|
|
210
|
+
.argument('<connectionId>', 'External Trigger Connection ID')
|
|
211
|
+
.action(async (connectionId) => {
|
|
212
|
+
try {
|
|
213
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
214
|
+
await client.deleteExternalConnection(orgId, stripPrllScheme(connectionId));
|
|
215
|
+
printJson({ ok: true });
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
printError(err);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
externalTriggers
|
|
222
|
+
.command('schema')
|
|
223
|
+
.description('Inspect CEL/template variables supported by a connection')
|
|
224
|
+
.argument('<connectionId>', 'External Trigger Connection ID')
|
|
225
|
+
.action(async (connectionId) => {
|
|
226
|
+
try {
|
|
227
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
228
|
+
const result = await client.getExternalTriggerSchema(orgId, stripPrllScheme(connectionId));
|
|
229
|
+
printJson(result);
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
printError(err);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
externalTriggers
|
|
236
|
+
.command('list')
|
|
237
|
+
.description('List External Triggers in the organization')
|
|
238
|
+
.option('--connection <connectionId>', 'Filter by External Trigger Connection ID')
|
|
239
|
+
.option('--creator-id <id>', 'Filter by creator user ID')
|
|
240
|
+
.option('--limit <n>', 'Maximum number of triggers to return', '50')
|
|
241
|
+
.option('--cursor <cursor>', 'Pagination cursor')
|
|
242
|
+
.action(async (opts) => {
|
|
243
|
+
try {
|
|
244
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
245
|
+
const result = await client.listExternalTriggers(orgId, stripUndefined({
|
|
246
|
+
connection_id: opts.connection ? stripPrllScheme(opts.connection) : undefined,
|
|
247
|
+
creator_id: opts.creatorId ? stripPrllScheme(opts.creatorId) : undefined,
|
|
248
|
+
limit: parsePositiveInt(opts.limit, '--limit'),
|
|
249
|
+
cursor: opts.cursor,
|
|
250
|
+
}));
|
|
251
|
+
printJson(result);
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
printError(err);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
externalTriggers
|
|
258
|
+
.command('create')
|
|
259
|
+
.description('Create an External Trigger targeting one or more agents')
|
|
260
|
+
.requiredOption('--connection <connectionId>', 'External Trigger Connection ID')
|
|
261
|
+
.requiredOption('--name <name>', 'Trigger name')
|
|
262
|
+
.requiredOption('--target-ids <ids>', 'Comma-separated target agent user IDs')
|
|
263
|
+
.option('--description <text>', 'Human-facing trigger description')
|
|
264
|
+
.option('--attached-to-uri <uri>', 'Attach to a prll:// resource URI')
|
|
265
|
+
.option('--filter <expr>', 'CEL filter expression', 'true')
|
|
266
|
+
.option('--filter-metadata-json <json>', 'JSON object with UI/display metadata for the filter')
|
|
267
|
+
.option('--template <text>', 'Liquid template for the agent input body')
|
|
268
|
+
.option('--template-file <path>', 'Read the Liquid template from a file')
|
|
269
|
+
.option('--max-runs <n>', 'Maximum delivered runs before the trigger auto-archives')
|
|
270
|
+
.option('--expires-at <ts>', 'Expiration time (RFC3339)')
|
|
271
|
+
.option('--client-context-json <json>', 'JSON object stored with the trigger')
|
|
272
|
+
.action(async (opts) => {
|
|
273
|
+
try {
|
|
274
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
275
|
+
const agentInputTemplate = await resolveTemplate({ ...opts, required: true });
|
|
276
|
+
const data = {
|
|
277
|
+
connection_id: stripPrllScheme(opts.connection),
|
|
278
|
+
name: opts.name,
|
|
279
|
+
target_agent_ids: parseIds(opts.targetIds, '--target-ids'),
|
|
280
|
+
filter_expr: opts.filter,
|
|
281
|
+
agent_input_template: agentInputTemplate ?? '',
|
|
282
|
+
...stripUndefined({
|
|
283
|
+
description: opts.description,
|
|
284
|
+
attached_to_uri: opts.attachedToUri,
|
|
285
|
+
filter_display_metadata: parseJsonObject(opts.filterMetadataJson, '--filter-metadata-json'),
|
|
286
|
+
max_runs: parsePositiveInt(opts.maxRuns, '--max-runs'),
|
|
287
|
+
expires_at: opts.expiresAt,
|
|
288
|
+
client_context: parseJsonObject(opts.clientContextJson, '--client-context-json'),
|
|
289
|
+
}),
|
|
290
|
+
};
|
|
291
|
+
const result = await client.createExternalTrigger(orgId, data);
|
|
292
|
+
printJson(result);
|
|
293
|
+
printRefHint(result.id);
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
printError(err);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
externalTriggers
|
|
300
|
+
.command('get')
|
|
301
|
+
.description('Get an External Trigger by ID')
|
|
302
|
+
.argument('<triggerId>', 'External Trigger ID')
|
|
303
|
+
.action(async (triggerId) => {
|
|
304
|
+
try {
|
|
305
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
306
|
+
const result = await client.getExternalTrigger(orgId, stripPrllScheme(triggerId));
|
|
307
|
+
printJson(result);
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
printError(err);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
externalTriggers
|
|
314
|
+
.command('update')
|
|
315
|
+
.description('Update an External Trigger')
|
|
316
|
+
.argument('<triggerId>', 'External Trigger ID')
|
|
317
|
+
.option('--name <name>', 'Trigger name')
|
|
318
|
+
.option('--description <text>', 'Human-facing trigger description')
|
|
319
|
+
.option('--status <status>', 'active | paused')
|
|
320
|
+
.option('--target-ids <ids>', 'Comma-separated target agent user IDs')
|
|
321
|
+
.option('--attached-to-uri <uri>', 'Attach to a prll:// resource URI')
|
|
322
|
+
.option('--clear-attached-to', 'Explicitly clear attached_to_uri')
|
|
323
|
+
.option('--filter <expr>', 'CEL filter expression')
|
|
324
|
+
.option('--filter-metadata-json <json>', 'JSON object with UI/display metadata for the filter')
|
|
325
|
+
.option('--template <text>', 'Liquid template for the agent input body')
|
|
326
|
+
.option('--template-file <path>', 'Read the Liquid template from a file')
|
|
327
|
+
.option('--max-runs <n>', 'Maximum delivered runs before the trigger auto-archives')
|
|
328
|
+
.option('--clear-max-runs', 'Explicitly clear max_runs')
|
|
329
|
+
.option('--expires-at <ts>', 'Expiration time (RFC3339)')
|
|
330
|
+
.option('--clear-expires-at', 'Explicitly clear expires_at')
|
|
331
|
+
.option('--client-context-json <json>', 'JSON object stored with the trigger')
|
|
332
|
+
.action(async (triggerId, opts) => {
|
|
333
|
+
try {
|
|
334
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
335
|
+
const conflicts = [
|
|
336
|
+
[opts.attachedToUri, opts.clearAttachedTo, '--attached-to-uri', '--clear-attached-to'],
|
|
337
|
+
[opts.maxRuns, opts.clearMaxRuns, '--max-runs', '--clear-max-runs'],
|
|
338
|
+
[opts.expiresAt, opts.clearExpiresAt, '--expires-at', '--clear-expires-at'],
|
|
339
|
+
];
|
|
340
|
+
for (const [value, clear, valueFlag, clearFlag] of conflicts) {
|
|
341
|
+
if (value !== undefined && clear) {
|
|
342
|
+
printError(new Error(`Do not pass ${valueFlag} together with ${clearFlag}.`));
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const agentInputTemplate = await resolveTemplate(opts);
|
|
347
|
+
const patch = stripUndefined({
|
|
348
|
+
name: opts.name,
|
|
349
|
+
description: opts.description,
|
|
350
|
+
status: parseEnum(opts.status, triggerStatuses, '--status'),
|
|
351
|
+
target_agent_ids: opts.targetIds !== undefined ? parseIds(opts.targetIds, '--target-ids') : undefined,
|
|
352
|
+
attached_to_uri: opts.attachedToUri,
|
|
353
|
+
attached_to_uri_clear: opts.clearAttachedTo || undefined,
|
|
354
|
+
filter_expr: opts.filter,
|
|
355
|
+
filter_display_metadata: parseJsonObject(opts.filterMetadataJson, '--filter-metadata-json'),
|
|
356
|
+
agent_input_template: agentInputTemplate,
|
|
357
|
+
max_runs: parsePositiveInt(opts.maxRuns, '--max-runs'),
|
|
358
|
+
max_runs_clear: opts.clearMaxRuns || undefined,
|
|
359
|
+
expires_at: opts.expiresAt,
|
|
360
|
+
expires_at_clear: opts.clearExpiresAt || undefined,
|
|
361
|
+
client_context: parseJsonObject(opts.clientContextJson, '--client-context-json'),
|
|
362
|
+
});
|
|
363
|
+
if (Object.keys(patch).length === 0) {
|
|
364
|
+
printError(new Error('No fields to update. Provide at least one option.'));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const result = await client.updateExternalTrigger(orgId, stripPrllScheme(triggerId), patch);
|
|
368
|
+
printJson(result);
|
|
369
|
+
}
|
|
370
|
+
catch (err) {
|
|
371
|
+
printError(err);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
externalTriggers
|
|
375
|
+
.command('pause')
|
|
376
|
+
.description('Pause an External Trigger')
|
|
377
|
+
.argument('<triggerId>', 'External Trigger ID')
|
|
378
|
+
.action(async (triggerId) => {
|
|
379
|
+
try {
|
|
380
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
381
|
+
const result = await client.updateExternalTrigger(orgId, stripPrllScheme(triggerId), {
|
|
382
|
+
status: 'paused',
|
|
383
|
+
});
|
|
384
|
+
printJson(result);
|
|
385
|
+
}
|
|
386
|
+
catch (err) {
|
|
387
|
+
printError(err);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
externalTriggers
|
|
391
|
+
.command('resume')
|
|
392
|
+
.description('Resume a paused External Trigger')
|
|
393
|
+
.argument('<triggerId>', 'External Trigger ID')
|
|
394
|
+
.action(async (triggerId) => {
|
|
395
|
+
try {
|
|
396
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
397
|
+
const result = await client.updateExternalTrigger(orgId, stripPrllScheme(triggerId), {
|
|
398
|
+
status: 'active',
|
|
399
|
+
});
|
|
400
|
+
printJson(result);
|
|
401
|
+
}
|
|
402
|
+
catch (err) {
|
|
403
|
+
printError(err);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
externalTriggers
|
|
407
|
+
.command('delete')
|
|
408
|
+
.description('Archive an External Trigger')
|
|
409
|
+
.argument('<triggerId>', 'External Trigger ID')
|
|
410
|
+
.action(async (triggerId) => {
|
|
411
|
+
try {
|
|
412
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
413
|
+
await client.deleteExternalTrigger(orgId, stripPrllScheme(triggerId));
|
|
414
|
+
printJson({ ok: true });
|
|
415
|
+
}
|
|
416
|
+
catch (err) {
|
|
417
|
+
printError(err);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
externalTriggers
|
|
421
|
+
.command('runs')
|
|
422
|
+
.description('List run history for an External Trigger')
|
|
423
|
+
.argument('<triggerId>', 'External Trigger ID')
|
|
424
|
+
.option('--limit <n>', 'Maximum number of runs to return', '50')
|
|
425
|
+
.option('--cursor <cursor>', 'Pagination cursor')
|
|
426
|
+
.action(async (triggerId, opts) => {
|
|
427
|
+
try {
|
|
428
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
429
|
+
const result = await client.listExternalTriggerRuns(orgId, stripUndefined({
|
|
430
|
+
trigger_id: stripPrllScheme(triggerId),
|
|
431
|
+
limit: parsePositiveInt(opts.limit, '--limit'),
|
|
432
|
+
cursor: opts.cursor,
|
|
433
|
+
}));
|
|
434
|
+
printJson(result);
|
|
435
|
+
}
|
|
436
|
+
catch (err) {
|
|
437
|
+
printError(err);
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
externalTriggers
|
|
441
|
+
.command('run')
|
|
442
|
+
.description('Get a single External Trigger run')
|
|
443
|
+
.argument('<runId>', 'External Trigger Run ID (xrn_...)')
|
|
444
|
+
.action(async (runId) => {
|
|
445
|
+
try {
|
|
446
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
447
|
+
const result = await client.getExternalTriggerRun(orgId, stripPrllScheme(runId));
|
|
448
|
+
printJson(result);
|
|
449
|
+
}
|
|
450
|
+
catch (err) {
|
|
451
|
+
printError(err);
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
externalTriggers
|
|
455
|
+
.command('events')
|
|
456
|
+
.description('List incoming events received by External Trigger Connections')
|
|
457
|
+
.option('--connection <connectionId>', 'Filter by External Trigger Connection ID')
|
|
458
|
+
.option('--limit <n>', 'Maximum number of events to return', '50')
|
|
459
|
+
.option('--cursor <cursor>', 'Pagination cursor')
|
|
460
|
+
.action(async (opts) => {
|
|
461
|
+
try {
|
|
462
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
463
|
+
const result = await client.listExternalIngressEvents(orgId, stripUndefined({
|
|
464
|
+
connection_id: opts.connection ? stripPrllScheme(opts.connection) : undefined,
|
|
465
|
+
limit: parsePositiveInt(opts.limit, '--limit'),
|
|
466
|
+
cursor: opts.cursor,
|
|
467
|
+
}));
|
|
468
|
+
printJson(result);
|
|
469
|
+
}
|
|
470
|
+
catch (err) {
|
|
471
|
+
printError(err);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
externalTriggers
|
|
475
|
+
.command('event')
|
|
476
|
+
.description('Get a single incoming event by ID')
|
|
477
|
+
.argument('<eventId>', 'External Trigger Event ID (xin_...)')
|
|
478
|
+
.action(async (eventId) => {
|
|
479
|
+
try {
|
|
480
|
+
const { client, orgId } = await resolveExternalTriggerCommandContext();
|
|
481
|
+
const result = await client.getExternalIngressEvent(orgId, stripPrllScheme(eventId));
|
|
482
|
+
printJson(result);
|
|
483
|
+
}
|
|
484
|
+
catch (err) {
|
|
485
|
+
printError(err);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/commands/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/commands/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAqTpD"}
|
package/dist/commands/tasks.js
CHANGED
|
@@ -148,6 +148,21 @@ export function registerTaskCommands(program) {
|
|
|
148
148
|
printError(err);
|
|
149
149
|
}
|
|
150
150
|
});
|
|
151
|
+
tasks
|
|
152
|
+
.command('assigned')
|
|
153
|
+
.description("List pending tasks (todo + in_progress) on a member's plate, including subtasks assigned to them")
|
|
154
|
+
.argument('[memberId]', 'Member user ID (defaults to the authenticated user)')
|
|
155
|
+
.action(async (memberId) => {
|
|
156
|
+
try {
|
|
157
|
+
const { client, orgId } = resolveCredentials();
|
|
158
|
+
const targetId = memberId ? stripPrllScheme(memberId) : (await client.getMe()).id;
|
|
159
|
+
const result = await client.getMemberTasksAll(orgId, targetId);
|
|
160
|
+
printJson(result);
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
printError(err);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
151
166
|
// ---- Comments subgroup ----
|
|
152
167
|
const comments = tasks.command('comments').description('Manage task comments');
|
|
153
168
|
comments
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wiki.d.ts","sourceRoot":"","sources":["../../src/commands/wiki.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wiki.d.ts","sourceRoot":"","sources":["../../src/commands/wiki.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyBpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAk/BpD"}
|
package/dist/commands/wiki.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
1
2
|
import { resolveCredentials, resolveRuntimeContext } from '../lib/client.js';
|
|
2
3
|
import { printError, printJson } from '../lib/output.js';
|
|
4
|
+
import { deleteWikiFile, downloadWikiFile, uploadWikiFile } from '../lib/wiki-files.js';
|
|
3
5
|
import { getAfcsDiff, getAfcsStatus, getWikiBlob, getWikiChangesetDetail, getWikiChangesetDiff, getWikiLog, getWikiOutline, getWikiSection, getWikiTree, listWikiChangesets, queryWiki, requestWikiAccess, resetWikiWorkspace, listWikis, proposeWikiChangeset, searchWiki, syncAllMounts, watchMounts, } from '../lib/wiki.js';
|
|
4
6
|
export function registerWikiCommands(program) {
|
|
5
7
|
const wiki = program.command('wiki').description('Parall Wiki — read, edit, and propose changes');
|
|
@@ -354,6 +356,94 @@ export function registerWikiCommands(program) {
|
|
|
354
356
|
printError(error);
|
|
355
357
|
}
|
|
356
358
|
});
|
|
359
|
+
// ---- Binary files (upload / get / delete) ----
|
|
360
|
+
const file = wiki
|
|
361
|
+
.command('file')
|
|
362
|
+
.description('Upload, download, and delete binary wiki files (images, PDFs, archives)');
|
|
363
|
+
file
|
|
364
|
+
.command('upload')
|
|
365
|
+
.description('Upload a binary file. Direct-commits to the default branch (maintain); with --changeset, writes into a proposal branch (read + author). Text files go through changesets, not uploads.')
|
|
366
|
+
.argument('<localPath>', 'Local file to upload')
|
|
367
|
+
.argument('<repoPath>', 'Destination path inside the wiki (e.g. assets/diagram.png)')
|
|
368
|
+
.argument('[wiki]', 'Wiki ID or slug (auto-resolves if org has one wiki)')
|
|
369
|
+
.option('--changeset <changesetId>', "Upload into this changeset's feature branch instead of the default branch")
|
|
370
|
+
.option('--message <message>', 'Commit message (default: "Upload <path>")')
|
|
371
|
+
.action(async (localPath, repoPath, wikiRef, options) => {
|
|
372
|
+
try {
|
|
373
|
+
const ctx = resolveCredentials();
|
|
374
|
+
const content = await fs.readFile(localPath);
|
|
375
|
+
const result = await uploadWikiFile(ctx, wikiRef, {
|
|
376
|
+
content,
|
|
377
|
+
toPath: repoPath,
|
|
378
|
+
changesetId: options.changeset,
|
|
379
|
+
message: options.message,
|
|
380
|
+
});
|
|
381
|
+
const where = result.target === 'changeset' ? `changeset ${result.changeset_id}` : 'default branch';
|
|
382
|
+
process.stderr.write(`Uploaded ${result.path} (${result.size} bytes, ${result.stored_as}) → ${where}\n`);
|
|
383
|
+
printJson(result);
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
printError(error);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
file
|
|
390
|
+
.command('get')
|
|
391
|
+
.description('Download a wiki file as raw bytes (LFS pointers are smudged to real bytes). Writes to stdout, or to --output. Use --ref to read a specific commit / changeset branch.')
|
|
392
|
+
.argument('<repoPath>', 'File path inside the wiki')
|
|
393
|
+
.argument('[wiki]', 'Wiki ID or slug (auto-resolves if org has one wiki)')
|
|
394
|
+
.option('--ref <ref>', 'Branch, commit SHA, or changeset branch ref (default: wiki default branch)')
|
|
395
|
+
.option('-o, --output <localPath>', 'Write bytes to this local file instead of stdout')
|
|
396
|
+
.action(async (repoPath, wikiRef, options) => {
|
|
397
|
+
try {
|
|
398
|
+
const ctx = resolveCredentials();
|
|
399
|
+
const result = await downloadWikiFile(ctx, wikiRef, {
|
|
400
|
+
path: repoPath,
|
|
401
|
+
ref: options.ref,
|
|
402
|
+
});
|
|
403
|
+
if (options.output) {
|
|
404
|
+
await fs.writeFile(options.output, result.content);
|
|
405
|
+
process.stderr.write(`Wrote ${result.size} bytes → ${options.output}\n`);
|
|
406
|
+
// Buffer is omitted from JSON output; report metadata + where it went.
|
|
407
|
+
printJson({
|
|
408
|
+
wiki_id: result.wiki_id,
|
|
409
|
+
wiki_slug: result.wiki_slug,
|
|
410
|
+
path: result.path,
|
|
411
|
+
ref: result.ref,
|
|
412
|
+
size: result.size,
|
|
413
|
+
output: options.output,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
// Raw bytes to stdout (binary-safe); metadata to stderr. No JSON on
|
|
418
|
+
// stdout here — it would corrupt the byte stream.
|
|
419
|
+
process.stderr.write(`${result.path} (${result.size} bytes${result.ref ? `, ref: ${result.ref}` : ''})\n`);
|
|
420
|
+
process.stdout.write(result.content);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
printError(error);
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
file
|
|
428
|
+
.command('delete')
|
|
429
|
+
.description('Delete a binary file from the wiki default branch (maintain). Text deletes must go through a changeset. The blob stays reachable in git history.')
|
|
430
|
+
.argument('<repoPath>', 'File path inside the wiki')
|
|
431
|
+
.argument('[wiki]', 'Wiki ID or slug (auto-resolves if org has one wiki)')
|
|
432
|
+
.option('--message <message>', 'Commit message (default: "Delete <path>")')
|
|
433
|
+
.action(async (repoPath, wikiRef, options) => {
|
|
434
|
+
try {
|
|
435
|
+
const ctx = resolveCredentials();
|
|
436
|
+
const result = await deleteWikiFile(ctx, wikiRef, {
|
|
437
|
+
path: repoPath,
|
|
438
|
+
message: options.message,
|
|
439
|
+
});
|
|
440
|
+
process.stderr.write(`Deleted ${result.path}\n`);
|
|
441
|
+
printJson(result);
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
printError(error);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
357
447
|
// ---- Content browsing (search, query, outline, section) ----
|
|
358
448
|
wiki
|
|
359
449
|
.command('search')
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { registerTaskCommands } from './commands/tasks.js';
|
|
|
10
10
|
import { registerCommentCommands } from './commands/comments.js';
|
|
11
11
|
import { registerProjectCommands } from './commands/projects.js';
|
|
12
12
|
import { registerScheduleCommands } from './commands/schedules.js';
|
|
13
|
+
import { registerExternalTriggerCommands } from './commands/external-triggers.js';
|
|
13
14
|
import { registerUserCommands } from './commands/users.js';
|
|
14
15
|
import { registerWikiCommands } from './commands/wiki.js';
|
|
15
16
|
import { registerMcpCommands } from './commands/mcp.js';
|
|
@@ -34,6 +35,7 @@ registerTaskCommands(program);
|
|
|
34
35
|
registerCommentCommands(program);
|
|
35
36
|
registerProjectCommands(program);
|
|
36
37
|
registerScheduleCommands(program);
|
|
38
|
+
registerExternalTriggerCommands(program);
|
|
37
39
|
registerUserCommands(program);
|
|
38
40
|
registerWikiCommands(program);
|
|
39
41
|
registerMcpCommands(program);
|
package/dist/lib/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,6DAA6D;AAC7D,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oGAAoG;IACpG,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,cAAc,CAiCtD;AAED,wBAAgB,kBAAkB,IAAI,mBAAmB,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,6DAA6D;AAC7D,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oGAAoG;IACpG,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,cAAc,CAiCtD;AAED,wBAAgB,kBAAkB,IAAI,mBAAmB,CA8BxD"}
|