@parall/cli 1.43.0 → 1.45.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.
@@ -1,3 +1,48 @@
1
+ import type { UpdateTaskRequest } from '@parall/sdk';
1
2
  import { Command } from 'commander';
3
+ export declare function buildCreateTaskData(opts: {
4
+ title: string;
5
+ description?: string;
6
+ status?: string;
7
+ priority?: string;
8
+ assigneeId?: string;
9
+ projectId?: string;
10
+ parentId?: string;
11
+ sourceChatId?: string;
12
+ dueDate?: string;
13
+ }): Partial<{
14
+ title: string;
15
+ description: string | undefined;
16
+ status: string | undefined;
17
+ priority: string | undefined;
18
+ assignee_id: string | undefined;
19
+ project_id: string | undefined;
20
+ parent_id: string | undefined;
21
+ source_chat_id: string | undefined;
22
+ due_date: string | undefined;
23
+ }>;
24
+ export declare function buildUpdateTaskData(opts: {
25
+ title?: string;
26
+ status?: string;
27
+ priority?: string;
28
+ assigneeId?: string;
29
+ description?: string;
30
+ projectId?: string;
31
+ parentId?: string;
32
+ sortOrder?: string;
33
+ placement?: string;
34
+ dueDate?: string;
35
+ }): Partial<{
36
+ title: string | undefined;
37
+ status: UpdateTaskRequest["status"];
38
+ priority: UpdateTaskRequest["priority"];
39
+ assignee_id: string | undefined;
40
+ description: string | undefined;
41
+ project_id: string | undefined;
42
+ parent_id: string | undefined;
43
+ sort_order: number | undefined;
44
+ placement: "end" | undefined;
45
+ due_date: string | null | undefined;
46
+ }>;
2
47
  export declare function registerTaskCommands(program: Command): void;
3
48
  //# sourceMappingURL=tasks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/commands/tasks.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAsWpD"}
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/commands/tasks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8DpC,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;;;;;;;;;;GAYA;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;;YAS0B,iBAAiB,CAAC,QAAQ,CAAC;cACvB,iBAAiB,CAAC,UAAU,CAAC;;;;;;;;GAS3D;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAiWpD"}
@@ -4,6 +4,92 @@ import { printJson, printError, printRefHint, stripPrllScheme } from '../lib/out
4
4
  function stripUndefined(obj) {
5
5
  return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
6
6
  }
7
+ // The CLI is an agent semantic adapter (parall-cli-design.md § Design
8
+ // Principles): it owns the agent-facing command grammar and rejects values
9
+ // outside it before any request, so local coercion can never turn invalid
10
+ // intent into a successful no-op. The server independently re-validates and
11
+ // stays authoritative for domain semantics (calendar validity, placement
12
+ // set, ordering conflicts, permissions).
13
+ /** Strict command grammar for --due-date; calendar validity (e.g. 2026-02-30)
14
+ * stays the server's domain (400 INVALID_DUE_DATE). */
15
+ const DUE_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
16
+ function resolveCreateDueDate(value) {
17
+ if (value === undefined)
18
+ return undefined;
19
+ if (!DUE_DATE_RE.test(value)) {
20
+ throw new Error('--due-date must be a YYYY-MM-DD date');
21
+ }
22
+ return value;
23
+ }
24
+ /** Update grammar adds the lowercase "none" sentinel — the agent-facing
25
+ * clear intent, mapped to an explicit null on the wire. */
26
+ function resolveUpdateDueDate(value) {
27
+ if (value === undefined)
28
+ return undefined;
29
+ if (value === 'none')
30
+ return null;
31
+ if (!DUE_DATE_RE.test(value)) {
32
+ throw new Error('--due-date must be a YYYY-MM-DD date, or "none" to clear it');
33
+ }
34
+ return value;
35
+ }
36
+ /** Only intents the CLI understands are accepted — a new placement value is a
37
+ * coordinated server + SDK + CLI + skill change, not a passthrough. The
38
+ * server's INVALID_PLACEMENT check remains the backstop for non-CLI callers. */
39
+ function resolvePlacement(value) {
40
+ if (value === undefined)
41
+ return undefined;
42
+ if (value !== 'end') {
43
+ throw new Error('--placement must be "end"');
44
+ }
45
+ return value;
46
+ }
47
+ /** Number('abc') is NaN and JSON.stringify(NaN) is null — without this guard
48
+ * a malformed --sort-order becomes sort_order:null, which the server treats
49
+ * as "field absent" and answers 200 without moving the task. */
50
+ function resolveSortOrder(value) {
51
+ if (value === undefined)
52
+ return undefined;
53
+ const trimmed = value.trim();
54
+ const parsed = trimmed === '' ? Number.NaN : Number(trimmed);
55
+ if (!Number.isFinite(parsed)) {
56
+ throw new Error('--sort-order must be a finite number');
57
+ }
58
+ return parsed;
59
+ }
60
+ export function buildCreateTaskData(opts) {
61
+ return stripUndefined({
62
+ title: opts.title,
63
+ description: opts.description,
64
+ status: opts.status,
65
+ priority: opts.priority,
66
+ assignee_id: opts.assigneeId ? stripPrllScheme(opts.assigneeId) : undefined,
67
+ project_id: opts.projectId ? stripPrllScheme(opts.projectId) : undefined,
68
+ parent_id: opts.parentId ? stripPrllScheme(opts.parentId) : undefined,
69
+ source_chat_id: opts.sourceChatId ? stripPrllScheme(opts.sourceChatId) : undefined,
70
+ due_date: resolveCreateDueDate(opts.dueDate),
71
+ });
72
+ }
73
+ export function buildUpdateTaskData(opts) {
74
+ // Mutually exclusive (the server also rejects the pair with 400
75
+ // CONFLICTING_SORT); fail fast client-side so an agent doesn't spend a
76
+ // request round-trip to find out.
77
+ if (opts.placement !== undefined && opts.sortOrder !== undefined) {
78
+ throw new Error('--placement and --sort-order are mutually exclusive. Provide only one.');
79
+ }
80
+ return stripUndefined({
81
+ title: opts.title,
82
+ status: opts.status,
83
+ priority: opts.priority,
84
+ assignee_id: opts.assigneeId ? stripPrllScheme(opts.assigneeId) : undefined,
85
+ description: opts.description,
86
+ project_id: opts.projectId ? stripPrllScheme(opts.projectId) : undefined,
87
+ parent_id: opts.parentId ? stripPrllScheme(opts.parentId) : undefined,
88
+ sort_order: resolveSortOrder(opts.sortOrder),
89
+ placement: resolvePlacement(opts.placement),
90
+ due_date: resolveUpdateDueDate(opts.dueDate),
91
+ });
92
+ }
7
93
  export function registerTaskCommands(program) {
8
94
  const tasks = program.command('tasks').description('Manage tasks');
9
95
  tasks
@@ -50,19 +136,13 @@ export function registerTaskCommands(program) {
50
136
  .option('--project-id <id>', 'Project ID')
51
137
  .option('--parent-id <id>', 'Parent task ID')
52
138
  .option('--source-chat-id <id>', 'Source chat ID')
139
+ .option('--due-date <date>', 'Planned completion date (YYYY-MM-DD)')
53
140
  .action(async (opts) => {
54
141
  try {
142
+ // Grammar first: an invalid option is a parameter error even when
143
+ // credentials are absent, and it must never reach the wire.
144
+ const data = buildCreateTaskData(opts);
55
145
  const { client, orgId } = resolveCredentials();
56
- const data = stripUndefined({
57
- title: opts.title,
58
- description: opts.description,
59
- status: opts.status,
60
- priority: opts.priority,
61
- assignee_id: opts.assigneeId ? stripPrllScheme(opts.assigneeId) : undefined,
62
- project_id: opts.projectId ? stripPrllScheme(opts.projectId) : undefined,
63
- parent_id: opts.parentId ? stripPrllScheme(opts.parentId) : undefined,
64
- source_chat_id: opts.sourceChatId ? stripPrllScheme(opts.sourceChatId) : undefined,
65
- });
66
146
  const result = await client.createTask(orgId, data);
67
147
  printJson(result);
68
148
  printRefHint(result.id);
@@ -96,24 +176,19 @@ export function registerTaskCommands(program) {
96
176
  .option('--description <text>', 'Task description')
97
177
  .option('--project-id <id>', 'Project ID')
98
178
  .option('--parent-id <id>', 'Parent task ID')
99
- .option('--sort-order <n>', 'Sort order')
179
+ .option('--sort-order <n>', 'Explicit sort_order value — only for pinpoint insertion relative to cards you can see; to append to a status column use --placement end')
180
+ .option('--placement <where>', 'Ordering intent — only "end" is accepted: appends the task to the end of its status column, position resolved by the server (mutually exclusive with --sort-order)')
181
+ .option('--due-date <date>', 'Planned completion date (YYYY-MM-DD), or "none" to clear it')
100
182
  .action(async (taskId, opts) => {
101
183
  try {
102
- taskId = stripPrllScheme(taskId);
103
- const { client, orgId } = resolveCredentials();
104
- const data = stripUndefined({
105
- title: opts.title,
106
- status: opts.status,
107
- priority: opts.priority,
108
- assignee_id: opts.assigneeId ? stripPrllScheme(opts.assigneeId) : undefined,
109
- description: opts.description,
110
- project_id: opts.projectId ? stripPrllScheme(opts.projectId) : undefined,
111
- parent_id: opts.parentId ? stripPrllScheme(opts.parentId) : undefined,
112
- sort_order: opts.sortOrder !== undefined ? Number(opts.sortOrder) : undefined,
113
- });
184
+ // Grammar first: an invalid option is a parameter error even when
185
+ // credentials are absent, and it must never reach the wire.
186
+ const data = buildUpdateTaskData(opts);
114
187
  if (Object.keys(data).length === 0) {
115
188
  printError(new Error('No fields to update. Provide at least one option.'));
116
189
  }
190
+ taskId = stripPrllScheme(taskId);
191
+ const { client, orgId } = resolveCredentials();
117
192
  // Typed dispatch binding: when this turn is a typed dispatch about
118
193
  // exactly this task, the update rides the claimed typed lane; the
119
194
  // first update also carries the canonical task_update:<dsp> effect
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/cli",
3
- "version": "1.43.0",
3
+ "version": "1.45.0",
4
4
  "description": "CLI client for Parall — universal agent & human access to Parall API",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,14 +36,14 @@
36
36
  "diff": "^8.0.3",
37
37
  "js-yaml": "^4.1.0",
38
38
  "zod": "^4.3.6",
39
- "@parall/agent-core": "1.43.0",
40
- "@parall/sdk": "1.43.0"
39
+ "@parall/agent-core": "1.45.0",
40
+ "@parall/sdk": "1.45.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/js-yaml": "^4.0.9",
44
44
  "@types/node": "^22.0.0",
45
45
  "typescript": "^5.7.0",
46
- "@parall/agent-core": "1.43.0"
46
+ "@parall/agent-core": "1.45.0"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "tsc",