@parall/cli 1.15.1 → 1.17.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/dm.d.ts.map +1 -1
- package/dist/commands/dm.js +25 -5
- package/dist/commands/files.d.ts +3 -0
- package/dist/commands/files.d.ts.map +1 -0
- package/dist/commands/files.js +66 -0
- package/dist/commands/messages.d.ts.map +1 -1
- package/dist/commands/messages.js +25 -3
- package/dist/commands/no-reply.d.ts +17 -0
- package/dist/commands/no-reply.d.ts.map +1 -0
- package/dist/commands/no-reply.js +29 -0
- package/dist/commands/tasks.js +8 -8
- package/dist/commands/wiki.d.ts.map +1 -1
- package/dist/commands/wiki.js +93 -0
- package/dist/index.js +4 -0
- package/dist/lib/upload.d.ts +9 -0
- package/dist/lib/upload.d.ts.map +1 -0
- package/dist/lib/upload.js +38 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dm.d.ts","sourceRoot":"","sources":["../../src/commands/dm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"dm.d.ts","sourceRoot":"","sources":["../../src/commands/dm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6BpC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,QAkDlD"}
|
package/dist/commands/dm.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveCredentials, resolveRuntimeContext } from '../lib/client.js';
|
|
2
2
|
import { printJson, printError, printRefHint, stripPrllScheme } from '../lib/output.js';
|
|
3
|
+
import { uploadFile } from '../lib/upload.js';
|
|
3
4
|
/**
|
|
4
5
|
* Resolve a name-or-id argument to a user ID.
|
|
5
6
|
* If it starts with "usr_", treat as ID. Otherwise, search org members by display_name.
|
|
@@ -24,22 +25,41 @@ export function registerDMCommands(program) {
|
|
|
24
25
|
.command('dm')
|
|
25
26
|
.description('Send a direct message to a user by name or ID (auto-creates chat if needed)')
|
|
26
27
|
.argument('<nameOrId>', 'Target user display name or ID (usr_...)')
|
|
27
|
-
.
|
|
28
|
+
.option('--text <text>', 'Message text')
|
|
29
|
+
.option('--file <path>', 'Upload and attach a local file')
|
|
30
|
+
.option('--attachment <id>', 'Attach an existing attachment (att_xxx or prll://att_xxx)')
|
|
28
31
|
.option('--no-reply', 'Hint that the recipient should not reply')
|
|
29
32
|
.action(async (nameOrId, opts) => {
|
|
30
33
|
try {
|
|
31
34
|
const { client, orgId } = resolveCredentials();
|
|
32
35
|
const ctx = resolveRuntimeContext();
|
|
33
36
|
const userId = await resolveUserId(client, orgId, nameOrId);
|
|
37
|
+
if (opts.file && opts.attachment) {
|
|
38
|
+
printError(new Error('--file and --attachment are mutually exclusive'));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const text = opts.text || '';
|
|
42
|
+
let attachmentIds;
|
|
43
|
+
if (opts.file) {
|
|
44
|
+
const result = await uploadFile(client, orgId, opts.file);
|
|
45
|
+
attachmentIds = [result.attachmentId];
|
|
46
|
+
}
|
|
47
|
+
else if (opts.attachment) {
|
|
48
|
+
attachmentIds = [stripPrllScheme(opts.attachment)];
|
|
49
|
+
}
|
|
50
|
+
if (!text && !attachmentIds) {
|
|
51
|
+
printError(new Error('Provide --text, --file, or --attachment'));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
34
54
|
const req = {
|
|
35
55
|
user_id: userId,
|
|
36
56
|
message_type: 'text',
|
|
37
|
-
content: { text
|
|
57
|
+
content: { text },
|
|
38
58
|
};
|
|
39
|
-
|
|
40
|
-
|
|
59
|
+
if (attachmentIds)
|
|
60
|
+
req.attachment_ids = attachmentIds;
|
|
61
|
+
if (opts.reply === false)
|
|
41
62
|
req.hints = { no_reply: true };
|
|
42
|
-
}
|
|
43
63
|
if (ctx.stepId)
|
|
44
64
|
req.agent_step_id = ctx.stepId;
|
|
45
65
|
const result = await client.sendDirectMessage(orgId, req);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/commands/files.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAoEpD"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { resolveCredentials } from '../lib/client.js';
|
|
4
|
+
import { printJson, printError, stripPrllScheme } from '../lib/output.js';
|
|
5
|
+
import { uploadFile } from '../lib/upload.js';
|
|
6
|
+
export function registerFileCommands(program) {
|
|
7
|
+
const files = program.command('files').description('Upload and download files');
|
|
8
|
+
files
|
|
9
|
+
.command('upload')
|
|
10
|
+
.description('Upload a file and return attachment metadata')
|
|
11
|
+
.argument('<path>', 'Local file path to upload')
|
|
12
|
+
.action(async (filePath) => {
|
|
13
|
+
try {
|
|
14
|
+
const { client, orgId } = resolveCredentials();
|
|
15
|
+
const result = await uploadFile(client, orgId, filePath);
|
|
16
|
+
printJson({
|
|
17
|
+
attachment_id: result.attachmentId,
|
|
18
|
+
file_name: result.fileName,
|
|
19
|
+
file_size: result.fileSize,
|
|
20
|
+
mime_type: result.mimeType,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
printError(err);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
files
|
|
28
|
+
.command('download')
|
|
29
|
+
.description('Download a file by attachment ID')
|
|
30
|
+
.argument('<attachmentId>', 'Attachment ID (att_xxx or prll://att_xxx)')
|
|
31
|
+
.option('-o, --output <path>', 'Output file path (default: ./{original_filename})')
|
|
32
|
+
.action(async (attachmentId, opts) => {
|
|
33
|
+
try {
|
|
34
|
+
const { client } = resolveCredentials();
|
|
35
|
+
const id = stripPrllScheme(attachmentId);
|
|
36
|
+
const fileInfo = await client.getFileUrl(id);
|
|
37
|
+
// Download the file
|
|
38
|
+
const res = await fetch(fileInfo.url);
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
throw new Error(`Download failed: ${res.status} ${res.statusText}`);
|
|
41
|
+
}
|
|
42
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
43
|
+
// Sanitize server-provided filename to prevent path traversal
|
|
44
|
+
const safeName = path.basename(fileInfo.file_name || id);
|
|
45
|
+
// Determine output path
|
|
46
|
+
const outputPath = opts.output
|
|
47
|
+
? path.resolve(opts.output)
|
|
48
|
+
: path.resolve(safeName);
|
|
49
|
+
// If output is a directory, write file inside it
|
|
50
|
+
let finalPath = outputPath;
|
|
51
|
+
if (fs.existsSync(outputPath) && fs.statSync(outputPath).isDirectory()) {
|
|
52
|
+
finalPath = path.join(outputPath, safeName);
|
|
53
|
+
}
|
|
54
|
+
fs.writeFileSync(finalPath, buffer);
|
|
55
|
+
printJson({
|
|
56
|
+
path: finalPath,
|
|
57
|
+
file_name: fileInfo.file_name,
|
|
58
|
+
file_size: fileInfo.file_size,
|
|
59
|
+
mime_type: fileInfo.mime_type,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
printError(err);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/commands/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/commands/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QAyHvD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveCredentials, resolveRuntimeContext } from '../lib/client.js';
|
|
2
2
|
import { printJson, printError, printRefHint, stripPrllScheme } from '../lib/output.js';
|
|
3
|
+
import { uploadFile } from '../lib/upload.js';
|
|
3
4
|
export function registerMessageCommands(program) {
|
|
4
5
|
const messages = program.command('messages').description('Manage messages');
|
|
5
6
|
messages
|
|
@@ -46,9 +47,11 @@ export function registerMessageCommands(program) {
|
|
|
46
47
|
});
|
|
47
48
|
messages
|
|
48
49
|
.command('send')
|
|
49
|
-
.description('Send a
|
|
50
|
+
.description('Send a message to a chat (text, file, or both)')
|
|
50
51
|
.argument('[chatId]', 'Chat ID (defaults to PRLL_CHAT_ID if set)')
|
|
51
|
-
.
|
|
52
|
+
.option('--text <text>', 'Message text')
|
|
53
|
+
.option('--file <path>', 'Upload and attach a local file')
|
|
54
|
+
.option('--attachment <id>', 'Attach an existing attachment (att_xxx or prll://att_xxx)')
|
|
52
55
|
.option('--thread-root-id <id>', 'Reply to a thread')
|
|
53
56
|
.option('--no-reply', 'Hint that the recipient should not reply')
|
|
54
57
|
.action(async (chatIdArg, opts) => {
|
|
@@ -60,10 +63,29 @@ export function registerMessageCommands(program) {
|
|
|
60
63
|
printError(new Error('Chat ID required — provide as argument or set PRLL_CHAT_ID'));
|
|
61
64
|
return;
|
|
62
65
|
}
|
|
66
|
+
if (opts.file && opts.attachment) {
|
|
67
|
+
printError(new Error('--file and --attachment are mutually exclusive'));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const text = opts.text || '';
|
|
71
|
+
let attachmentIds;
|
|
72
|
+
if (opts.file) {
|
|
73
|
+
const result = await uploadFile(client, orgId, opts.file);
|
|
74
|
+
attachmentIds = [result.attachmentId];
|
|
75
|
+
}
|
|
76
|
+
else if (opts.attachment) {
|
|
77
|
+
attachmentIds = [stripPrllScheme(opts.attachment)];
|
|
78
|
+
}
|
|
79
|
+
if (!text && !attachmentIds) {
|
|
80
|
+
printError(new Error('Provide --text, --file, or --attachment'));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
63
83
|
const req = {
|
|
64
84
|
message_type: 'text',
|
|
65
|
-
content: { text
|
|
85
|
+
content: { text },
|
|
66
86
|
};
|
|
87
|
+
if (attachmentIds)
|
|
88
|
+
req.attachment_ids = attachmentIds;
|
|
67
89
|
if (opts.threadRootId !== undefined)
|
|
68
90
|
req.thread_root_id = stripPrllScheme(opts.threadRootId);
|
|
69
91
|
if (opts.reply === false)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* `parall no-reply` — explicit "I am not replying this turn" audit signal.
|
|
4
|
+
*
|
|
5
|
+
* Runs entirely client-side: no API call, no server state. Under the unified
|
|
6
|
+
* runtime output contract (see `docs/engineering-design/agent-dm-loop-prevention.md`
|
|
7
|
+
* § Layer 0), plain text is never auto-projected as a chat message — agents
|
|
8
|
+
* produce visible messages only by explicitly invoking `parall messages send`
|
|
9
|
+
* / `dm`. So silence is the default when the agent simply does nothing.
|
|
10
|
+
*
|
|
11
|
+
* What this command adds: a deterministic, auditable marker in the agent's
|
|
12
|
+
* session tool-call log saying "this turn was intentionally silent, here's
|
|
13
|
+
* the optional reason," which helps operators distinguish deliberate silence
|
|
14
|
+
* from a crashed or timed-out dispatch.
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerNoReplyCommands(program: Command): void;
|
|
17
|
+
//# sourceMappingURL=no-reply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-reply.d.ts","sourceRoot":"","sources":["../../src/commands/no-reply.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QAevD"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `parall no-reply` — explicit "I am not replying this turn" audit signal.
|
|
3
|
+
*
|
|
4
|
+
* Runs entirely client-side: no API call, no server state. Under the unified
|
|
5
|
+
* runtime output contract (see `docs/engineering-design/agent-dm-loop-prevention.md`
|
|
6
|
+
* § Layer 0), plain text is never auto-projected as a chat message — agents
|
|
7
|
+
* produce visible messages only by explicitly invoking `parall messages send`
|
|
8
|
+
* / `dm`. So silence is the default when the agent simply does nothing.
|
|
9
|
+
*
|
|
10
|
+
* What this command adds: a deterministic, auditable marker in the agent's
|
|
11
|
+
* session tool-call log saying "this turn was intentionally silent, here's
|
|
12
|
+
* the optional reason," which helps operators distinguish deliberate silence
|
|
13
|
+
* from a crashed or timed-out dispatch.
|
|
14
|
+
*/
|
|
15
|
+
export function registerNoReplyCommands(program) {
|
|
16
|
+
program
|
|
17
|
+
.command('no-reply')
|
|
18
|
+
.description('Record an explicit "no visible reply this turn" marker in the session log. ' +
|
|
19
|
+
'Purely informational — silence is already the default when no `messages send` runs.')
|
|
20
|
+
.option('--reason <reason>', 'Optional human-readable reason, logged for debugging')
|
|
21
|
+
.action((opts) => {
|
|
22
|
+
// Print a deterministic marker so operators reading tool_use logs can
|
|
23
|
+
// confirm the agent deliberately declared silence this turn.
|
|
24
|
+
const payload = { ack: 'no-reply' };
|
|
25
|
+
if (opts.reason)
|
|
26
|
+
payload.reason = opts.reason;
|
|
27
|
+
process.stdout.write(JSON.stringify(payload) + '\n');
|
|
28
|
+
});
|
|
29
|
+
}
|
package/dist/commands/tasks.js
CHANGED
|
@@ -165,7 +165,8 @@ export function registerTaskCommands(program) {
|
|
|
165
165
|
cursor: opts.cursor,
|
|
166
166
|
order: opts.order,
|
|
167
167
|
});
|
|
168
|
-
const
|
|
168
|
+
const { COMMENT_TARGET } = await import('@parall/sdk');
|
|
169
|
+
const result = await client.getComments(orgId, { target_uri: COMMENT_TARGET.task(stripPrllScheme(taskId)), ...params });
|
|
169
170
|
printJson(result);
|
|
170
171
|
}
|
|
171
172
|
catch (err) {
|
|
@@ -180,7 +181,8 @@ export function registerTaskCommands(program) {
|
|
|
180
181
|
.action(async (taskId, opts) => {
|
|
181
182
|
try {
|
|
182
183
|
const { client, orgId } = resolveCredentials();
|
|
183
|
-
const
|
|
184
|
+
const { COMMENT_TARGET } = await import('@parall/sdk');
|
|
185
|
+
const result = await client.createComment(orgId, { target_uri: COMMENT_TARGET.task(stripPrllScheme(taskId)), body: opts.body });
|
|
184
186
|
printJson(result);
|
|
185
187
|
printRefHint(result.id);
|
|
186
188
|
}
|
|
@@ -191,13 +193,12 @@ export function registerTaskCommands(program) {
|
|
|
191
193
|
comments
|
|
192
194
|
.command('update')
|
|
193
195
|
.description('Update a task comment')
|
|
194
|
-
.argument('<taskId>', 'Task ID')
|
|
195
196
|
.argument('<commentId>', 'Comment ID')
|
|
196
197
|
.requiredOption('--body <text>', 'New comment body')
|
|
197
|
-
.action(async (
|
|
198
|
+
.action(async (commentId, opts) => {
|
|
198
199
|
try {
|
|
199
200
|
const { client, orgId } = resolveCredentials();
|
|
200
|
-
const result = await client.
|
|
201
|
+
const result = await client.updateComment(orgId, stripPrllScheme(commentId), { body: opts.body });
|
|
201
202
|
printJson(result);
|
|
202
203
|
}
|
|
203
204
|
catch (err) {
|
|
@@ -207,12 +208,11 @@ export function registerTaskCommands(program) {
|
|
|
207
208
|
comments
|
|
208
209
|
.command('delete')
|
|
209
210
|
.description('Delete a task comment')
|
|
210
|
-
.argument('<taskId>', 'Task ID')
|
|
211
211
|
.argument('<commentId>', 'Comment ID')
|
|
212
|
-
.action(async (
|
|
212
|
+
.action(async (commentId) => {
|
|
213
213
|
try {
|
|
214
214
|
const { client, orgId } = resolveCredentials();
|
|
215
|
-
await client.
|
|
215
|
+
await client.deleteComment(orgId, stripPrllScheme(commentId));
|
|
216
216
|
printJson({ ok: true });
|
|
217
217
|
}
|
|
218
218
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wiki.d.ts","sourceRoot":"","sources":["../../src/commands/wiki.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"wiki.d.ts","sourceRoot":"","sources":["../../src/commands/wiki.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAwwBpD"}
|
package/dist/commands/wiki.js
CHANGED
|
@@ -630,6 +630,99 @@ export function registerWikiCommands(program) {
|
|
|
630
630
|
printError(error);
|
|
631
631
|
}
|
|
632
632
|
});
|
|
633
|
+
// ---- Comments ----
|
|
634
|
+
const comment = wiki.command('comment').description('Manage wiki comments');
|
|
635
|
+
comment
|
|
636
|
+
.command('list')
|
|
637
|
+
.description('List comments on a wiki target')
|
|
638
|
+
.argument('[wiki]', 'Wiki ID or slug')
|
|
639
|
+
.requiredOption('--target-type <type>', 'Target type (changeset or page)')
|
|
640
|
+
.requiredOption('--target-id <id>', 'Target ID (changeset ID or page path)')
|
|
641
|
+
.option('--limit <n>', 'Maximum number of comments to return', '50')
|
|
642
|
+
.option('--cursor <cursor>', 'Pagination cursor')
|
|
643
|
+
.option('--order <order>', 'Sort order (asc or desc)')
|
|
644
|
+
.action(async (wikiRef, opts) => {
|
|
645
|
+
try {
|
|
646
|
+
const ctx = resolveCredentials();
|
|
647
|
+
const { resolveWikiRefOrDefault } = await import('../lib/wiki.js');
|
|
648
|
+
const { COMMENT_TARGET } = await import('@parall/sdk');
|
|
649
|
+
const w = await resolveWikiRefOrDefault(ctx, wikiRef);
|
|
650
|
+
if (opts.targetType !== 'changeset' && opts.targetType !== 'page') {
|
|
651
|
+
throw new Error('--target-type must be "changeset" or "page"');
|
|
652
|
+
}
|
|
653
|
+
const targetUri = opts.targetType === 'changeset'
|
|
654
|
+
? COMMENT_TARGET.changeset(opts.targetId, w.id)
|
|
655
|
+
: COMMENT_TARGET.wikiPage(w.id, opts.targetId);
|
|
656
|
+
const result = await ctx.client.getComments(ctx.orgId, {
|
|
657
|
+
target_uri: targetUri,
|
|
658
|
+
limit: Number(opts.limit),
|
|
659
|
+
cursor: opts.cursor,
|
|
660
|
+
order: opts.order,
|
|
661
|
+
});
|
|
662
|
+
printJson(result);
|
|
663
|
+
}
|
|
664
|
+
catch (err) {
|
|
665
|
+
printError(err);
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
comment
|
|
669
|
+
.command('add')
|
|
670
|
+
.description('Add a comment to a wiki target')
|
|
671
|
+
.argument('[wiki]', 'Wiki ID or slug')
|
|
672
|
+
.requiredOption('--target-type <type>', 'Target type (changeset or page)')
|
|
673
|
+
.requiredOption('--target-id <id>', 'Target ID (changeset ID or page path)')
|
|
674
|
+
.requiredOption('--body <text>', 'Comment body')
|
|
675
|
+
.action(async (wikiRef, opts) => {
|
|
676
|
+
try {
|
|
677
|
+
const ctx = resolveCredentials();
|
|
678
|
+
const { resolveWikiRefOrDefault } = await import('../lib/wiki.js');
|
|
679
|
+
const { COMMENT_TARGET } = await import('@parall/sdk');
|
|
680
|
+
const w = await resolveWikiRefOrDefault(ctx, wikiRef);
|
|
681
|
+
if (opts.targetType !== 'changeset' && opts.targetType !== 'page') {
|
|
682
|
+
throw new Error('--target-type must be "changeset" or "page"');
|
|
683
|
+
}
|
|
684
|
+
const targetUri = opts.targetType === 'changeset'
|
|
685
|
+
? COMMENT_TARGET.changeset(opts.targetId, w.id)
|
|
686
|
+
: COMMENT_TARGET.wikiPage(w.id, opts.targetId);
|
|
687
|
+
const result = await ctx.client.createComment(ctx.orgId, {
|
|
688
|
+
target_uri: targetUri,
|
|
689
|
+
body: opts.body,
|
|
690
|
+
});
|
|
691
|
+
printJson(result);
|
|
692
|
+
}
|
|
693
|
+
catch (err) {
|
|
694
|
+
printError(err);
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
comment
|
|
698
|
+
.command('update')
|
|
699
|
+
.description('Update a wiki comment')
|
|
700
|
+
.argument('<commentId>', 'Comment ID')
|
|
701
|
+
.requiredOption('--body <text>', 'New comment body')
|
|
702
|
+
.action(async (commentId, opts) => {
|
|
703
|
+
try {
|
|
704
|
+
const ctx = resolveCredentials();
|
|
705
|
+
const result = await ctx.client.updateComment(ctx.orgId, commentId, { body: opts.body });
|
|
706
|
+
printJson(result);
|
|
707
|
+
}
|
|
708
|
+
catch (err) {
|
|
709
|
+
printError(err);
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
comment
|
|
713
|
+
.command('delete')
|
|
714
|
+
.description('Delete a wiki comment')
|
|
715
|
+
.argument('<commentId>', 'Comment ID')
|
|
716
|
+
.action(async (commentId) => {
|
|
717
|
+
try {
|
|
718
|
+
const ctx = resolveCredentials();
|
|
719
|
+
await ctx.client.deleteComment(ctx.orgId, commentId);
|
|
720
|
+
printJson({ ok: true });
|
|
721
|
+
}
|
|
722
|
+
catch (err) {
|
|
723
|
+
printError(err);
|
|
724
|
+
}
|
|
725
|
+
});
|
|
633
726
|
// ---- Utility (not for direct agent use) ----
|
|
634
727
|
wiki
|
|
635
728
|
.command('list')
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,9 @@ import { registerProjectCommands } from './commands/projects.js';
|
|
|
10
10
|
import { registerUserCommands } from './commands/users.js';
|
|
11
11
|
import { registerWikiCommands } from './commands/wiki.js';
|
|
12
12
|
import { registerMcpCommands } from './commands/mcp.js';
|
|
13
|
+
import { registerNoReplyCommands } from './commands/no-reply.js';
|
|
13
14
|
import { registerRefCommands } from './commands/refs.js';
|
|
15
|
+
import { registerFileCommands } from './commands/files.js';
|
|
14
16
|
const require = createRequire(import.meta.url);
|
|
15
17
|
const pkg = require('../package.json');
|
|
16
18
|
const program = new Command();
|
|
@@ -27,5 +29,7 @@ registerProjectCommands(program);
|
|
|
27
29
|
registerUserCommands(program);
|
|
28
30
|
registerWikiCommands(program);
|
|
29
31
|
registerMcpCommands(program);
|
|
32
|
+
registerNoReplyCommands(program);
|
|
30
33
|
registerRefCommands(program);
|
|
34
|
+
registerFileCommands(program);
|
|
31
35
|
program.parse();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ParallClient } from '@parall/sdk';
|
|
2
|
+
export declare function inferMimeType(filePath: string): string;
|
|
3
|
+
export declare function uploadFile(client: ParallClient, orgId: string, filePath: string): Promise<{
|
|
4
|
+
attachmentId: string;
|
|
5
|
+
fileName: string;
|
|
6
|
+
fileSize: number;
|
|
7
|
+
mimeType: string;
|
|
8
|
+
}>;
|
|
9
|
+
//# sourceMappingURL=upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/lib/upload.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAyB/K"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
const MIME_MAP = {
|
|
4
|
+
'.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif',
|
|
5
|
+
'.webp': 'image/webp', '.svg': 'image/svg+xml', '.pdf': 'application/pdf',
|
|
6
|
+
'.json': 'application/json', '.txt': 'text/plain', '.md': 'text/markdown',
|
|
7
|
+
'.csv': 'text/csv', '.zip': 'application/zip', '.tar': 'application/x-tar',
|
|
8
|
+
'.gz': 'application/gzip', '.mp4': 'video/mp4', '.webm': 'video/webm',
|
|
9
|
+
'.mp3': 'audio/mpeg', '.wav': 'audio/wav', '.html': 'text/html',
|
|
10
|
+
'.js': 'text/javascript', '.ts': 'text/typescript', '.go': 'text/x-go',
|
|
11
|
+
'.py': 'text/x-python', '.rs': 'text/x-rust',
|
|
12
|
+
};
|
|
13
|
+
export function inferMimeType(filePath) {
|
|
14
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
15
|
+
return MIME_MAP[ext] || 'application/octet-stream';
|
|
16
|
+
}
|
|
17
|
+
export async function uploadFile(client, orgId, filePath) {
|
|
18
|
+
const resolved = path.resolve(filePath);
|
|
19
|
+
const stat = fs.statSync(resolved);
|
|
20
|
+
const fileName = path.basename(resolved);
|
|
21
|
+
const mimeType = inferMimeType(fileName);
|
|
22
|
+
const presign = await client.getUploadPresignUrl(orgId, {
|
|
23
|
+
file_name: fileName,
|
|
24
|
+
file_size: stat.size,
|
|
25
|
+
mime_type: mimeType,
|
|
26
|
+
});
|
|
27
|
+
const fileBuffer = fs.readFileSync(resolved);
|
|
28
|
+
const uploadRes = await fetch(presign.upload_url, {
|
|
29
|
+
method: 'PUT',
|
|
30
|
+
body: fileBuffer,
|
|
31
|
+
headers: { 'Content-Type': mimeType },
|
|
32
|
+
});
|
|
33
|
+
if (!uploadRes.ok) {
|
|
34
|
+
throw new Error(`Upload failed: ${uploadRes.status} ${uploadRes.statusText}`);
|
|
35
|
+
}
|
|
36
|
+
await client.completeUpload(orgId, presign.attachment_id);
|
|
37
|
+
return { attachmentId: presign.attachment_id, fileName, fileSize: stat.size, mimeType };
|
|
38
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "CLI client for Parall — universal agent & human access to Parall API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@modelcontextprotocol/sdk": "1.27.1",
|
|
31
31
|
"commander": "^13.0.0",
|
|
32
32
|
"zod": "^4.3.6",
|
|
33
|
-
"@parall/sdk": "1.
|
|
33
|
+
"@parall/sdk": "1.17.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^22.0.0",
|