@parall/cli 1.44.0 → 1.46.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 +1 @@
1
- {"version":3,"file":"clip.d.ts","sourceRoot":"","sources":["../../src/commands/clip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8BpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAuJpD"}
1
+ {"version":3,"file":"clip.d.ts","sourceRoot":"","sources":["../../src/commands/clip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+BpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QA0JpD"}
@@ -1,4 +1,5 @@
1
1
  import { resolveCredentials } from '../lib/client.js';
2
+ import { invokeClipAwaitingActivation } from '../lib/clip-invoke.js';
2
3
  import { printJson, printError } from '../lib/output.js';
3
4
  /**
4
5
  * Derive a CLI-friendly alias from a registry source string.
@@ -143,7 +144,10 @@ export function registerClipCommands(program) {
143
144
  input: parsedInput,
144
145
  timeout_ms: timeoutMs,
145
146
  };
146
- const result = await client.invokeClip(orgId, req);
147
+ // Hosted-browser clips may hit a cold profile: absorb the (pre-dispatch,
148
+ // side-effect-free) BROWSER_PROFILE_ACTIVATING window here instead of
149
+ // making every agent hand-roll a retry loop. See lib/clip-invoke.ts.
150
+ const result = await invokeClipAwaitingActivation(client, orgId, req);
147
151
  if (result.error) {
148
152
  printError(new Error(result.error));
149
153
  }
@@ -0,0 +1,10 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * `parall slack …` — the per-vendor platform verb for a channel whose vendor
4
+ * ships no agent-grade CLI (tier B in the multi-channel architecture). The
5
+ * agent sends as its bound bot identity; Slack credentials never reach the
6
+ * runtime — api-server performs the vendor call in-process.
7
+ * Design: docs/engineering-design/multi-channel-architecture-design.md §8.2.
8
+ */
9
+ export declare function registerSlackCommands(program: Command): void;
10
+ //# sourceMappingURL=slack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../src/commands/slack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,QA+BrD"}
@@ -0,0 +1,35 @@
1
+ import { resolveCredentials } from '../lib/client.js';
2
+ import { printError, printJson } from '../lib/output.js';
3
+ /**
4
+ * `parall slack …` — the per-vendor platform verb for a channel whose vendor
5
+ * ships no agent-grade CLI (tier B in the multi-channel architecture). The
6
+ * agent sends as its bound bot identity; Slack credentials never reach the
7
+ * runtime — api-server performs the vendor call in-process.
8
+ * Design: docs/engineering-design/multi-channel-architecture-design.md §8.2.
9
+ */
10
+ export function registerSlackCommands(program) {
11
+ const slack = program
12
+ .command('slack')
13
+ .description('Slack channel platform verbs (agent-only; sends as the bound bot)');
14
+ slack
15
+ .command('send')
16
+ .description('Send a message to a Slack conversation this connection has seen inbound')
17
+ .requiredOption('--channel <channelId>', 'Vendor-native conversation id from the inbound event (C…/G…/D…)')
18
+ .requiredOption('--text <text>', 'Message text (plain text)')
19
+ .option('--reply-to <messageId>', 'Inbound message id you are answering ({channel}:{ts}); REQUIRED in channels (reply lands in its thread), optional in DMs (always linear)')
20
+ .action(async (opts) => {
21
+ try {
22
+ const { client, orgId } = resolveCredentials();
23
+ const sent = await client.sendChannelMessage(orgId, {
24
+ channel_type: 'slack',
25
+ conversation_id: opts.channel,
26
+ text: opts.text,
27
+ ...(opts.replyTo ? { reply_to: opts.replyTo } : {}),
28
+ });
29
+ printJson(sent);
30
+ }
31
+ catch (err) {
32
+ printError(err);
33
+ }
34
+ });
35
+ }
@@ -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/dist/index.js CHANGED
@@ -20,6 +20,7 @@ import { registerSearchCommands } from './commands/search.js';
20
20
  import { registerFileCommands } from './commands/files.js';
21
21
  import { registerMachineCommands } from './commands/machines.js';
22
22
  import { registerClipCommands } from './commands/clip.js';
23
+ import { registerSlackCommands } from './commands/slack.js';
23
24
  const require = createRequire(import.meta.url);
24
25
  const pkg = require('../package.json');
25
26
  const program = new Command();
@@ -46,4 +47,5 @@ registerSearchCommands(program);
46
47
  registerFileCommands(program);
47
48
  registerMachineCommands(program);
48
49
  registerClipCommands(program);
50
+ registerSlackCommands(program);
49
51
  program.parse();
@@ -0,0 +1,45 @@
1
+ import type { InvokeClipRequest, InvokeClipResponse, ParallClient } from '@parall/sdk';
2
+ /**
3
+ * Hosted browser activation wait — CLI-side bounded retry for `clip invoke`.
4
+ *
5
+ * A clip bound to a `placement=hosted` BrowserProfile whose pod is cold returns
6
+ * 503 BROWSER_PROFILE_ACTIVATING from the invoke pre-flight: the controller is
7
+ * spawning + registering the pod, and the clip command has NOT been dispatched
8
+ * yet, so retrying the same request cannot double-execute anything. This module
9
+ * absorbs that one precise error so a single `parall clip invoke` rides through
10
+ * cold start instead of forcing the caller (agent or human) into a blind
11
+ * retry loop.
12
+ *
13
+ * Deliberately NOT in the SDK: `invokeClip()` keeps its throw-on-503 semantics
14
+ * for every other consumer; only the CLI invoke behavior waits.
15
+ */
16
+ /** Total activation wait budget. Independent of the clip command --timeout:
17
+ * each attempt keeps its own full command timeout, so worst-case wall clock is
18
+ * roughly this budget plus one command timeout. */
19
+ export declare const ACTIVATION_WAIT_BUDGET_MS = 60000;
20
+ /** Backoff schedule between activation retries; the last entry repeats. */
21
+ export declare const ACTIVATION_BACKOFF_MS: number[];
22
+ /** Injectable time hooks so tests never really sleep. */
23
+ export type ActivationWaitHooks = {
24
+ sleep?: (ms: number) => Promise<void>;
25
+ now?: () => number;
26
+ };
27
+ /**
28
+ * The ONLY retryable error: a typed ApiError carrying exactly 503 +
29
+ * BROWSER_PROFILE_ACTIVATING. Everything else — consent, permission, provider
30
+ * offline/not-running, command errors, timeouts, other 5xx — means retrying is
31
+ * either pointless or unsafe (the command may have been dispatched), so it
32
+ * propagates unchanged on the first occurrence.
33
+ */
34
+ export declare function isActivationPending(err: unknown): boolean;
35
+ /**
36
+ * Invoke a clip, transparently absorbing the hosted-browser activation window.
37
+ *
38
+ * Retries the identical request while the server reports
39
+ * BROWSER_PROFILE_ACTIVATING, backing off 500ms → 1s → 2s (capped) until the
40
+ * 60s activation budget is spent. Once spent, the LAST typed ApiError is
41
+ * rethrown as-is — status/code intact — so the printed error stays
42
+ * machine-readable. Any other failure is rethrown immediately.
43
+ */
44
+ export declare function invokeClipAwaitingActivation(client: Pick<ParallClient, 'invokeClip'>, orgId: string, req: InvokeClipRequest, hooks?: ActivationWaitHooks): Promise<InvokeClipResponse>;
45
+ //# sourceMappingURL=clip-invoke.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clip-invoke.d.ts","sourceRoot":"","sources":["../../src/lib/clip-invoke.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGvF;;;;;;;;;;;;;GAaG;AAEH;;oDAEoD;AACpD,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,2EAA2E;AAC3E,eAAO,MAAM,qBAAqB,UAAsB,CAAC;AAEzD,yDAAyD;AACzD,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,CAAC;AAIF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEzD;AAED;;;;;;;;GAQG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,EACxC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,iBAAiB,EACtB,KAAK,CAAC,EAAE,mBAAmB,GAC1B,OAAO,CAAC,kBAAkB,CAAC,CAiB7B"}
@@ -0,0 +1,61 @@
1
+ import { ApiError } from '@parall/sdk';
2
+ /**
3
+ * Hosted browser activation wait — CLI-side bounded retry for `clip invoke`.
4
+ *
5
+ * A clip bound to a `placement=hosted` BrowserProfile whose pod is cold returns
6
+ * 503 BROWSER_PROFILE_ACTIVATING from the invoke pre-flight: the controller is
7
+ * spawning + registering the pod, and the clip command has NOT been dispatched
8
+ * yet, so retrying the same request cannot double-execute anything. This module
9
+ * absorbs that one precise error so a single `parall clip invoke` rides through
10
+ * cold start instead of forcing the caller (agent or human) into a blind
11
+ * retry loop.
12
+ *
13
+ * Deliberately NOT in the SDK: `invokeClip()` keeps its throw-on-503 semantics
14
+ * for every other consumer; only the CLI invoke behavior waits.
15
+ */
16
+ /** Total activation wait budget. Independent of the clip command --timeout:
17
+ * each attempt keeps its own full command timeout, so worst-case wall clock is
18
+ * roughly this budget plus one command timeout. */
19
+ export const ACTIVATION_WAIT_BUDGET_MS = 60_000;
20
+ /** Backoff schedule between activation retries; the last entry repeats. */
21
+ export const ACTIVATION_BACKOFF_MS = [500, 1_000, 2_000];
22
+ const realSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
23
+ /**
24
+ * The ONLY retryable error: a typed ApiError carrying exactly 503 +
25
+ * BROWSER_PROFILE_ACTIVATING. Everything else — consent, permission, provider
26
+ * offline/not-running, command errors, timeouts, other 5xx — means retrying is
27
+ * either pointless or unsafe (the command may have been dispatched), so it
28
+ * propagates unchanged on the first occurrence.
29
+ */
30
+ export function isActivationPending(err) {
31
+ return err instanceof ApiError && err.status === 503 && err.code === 'BROWSER_PROFILE_ACTIVATING';
32
+ }
33
+ /**
34
+ * Invoke a clip, transparently absorbing the hosted-browser activation window.
35
+ *
36
+ * Retries the identical request while the server reports
37
+ * BROWSER_PROFILE_ACTIVATING, backing off 500ms → 1s → 2s (capped) until the
38
+ * 60s activation budget is spent. Once spent, the LAST typed ApiError is
39
+ * rethrown as-is — status/code intact — so the printed error stays
40
+ * machine-readable. Any other failure is rethrown immediately.
41
+ */
42
+ export async function invokeClipAwaitingActivation(client, orgId, req, hooks) {
43
+ const sleep = hooks?.sleep ?? realSleep;
44
+ const now = hooks?.now ?? Date.now;
45
+ const deadline = now() + ACTIVATION_WAIT_BUDGET_MS;
46
+ for (let attempt = 0;; attempt++) {
47
+ try {
48
+ return await client.invokeClip(orgId, req);
49
+ }
50
+ catch (err) {
51
+ if (!isActivationPending(err))
52
+ throw err;
53
+ const remaining = deadline - now();
54
+ if (remaining <= 0)
55
+ throw err;
56
+ const backoff = ACTIVATION_BACKOFF_MS[Math.min(attempt, ACTIVATION_BACKOFF_MS.length - 1)];
57
+ // Clamp to the remaining budget so the final sleep can't overrun it.
58
+ await sleep(Math.min(backoff, remaining));
59
+ }
60
+ }
61
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/cli",
3
- "version": "1.44.0",
3
+ "version": "1.46.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.44.0",
40
- "@parall/sdk": "1.44.0"
39
+ "@parall/agent-core": "1.46.0",
40
+ "@parall/sdk": "1.46.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.44.0"
46
+ "@parall/agent-core": "1.46.0"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "tsc",