sim 2.1.8-preview.98.1 → 2.1.8-preview.99.1

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.
Files changed (2) hide show
  1. package/dist/index.js +126 -55
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4460,6 +4460,22 @@ function toApiError(url, status, contentType, raw) {
4460
4460
  function truncate(value, max) {
4461
4461
  return value.length <= max ? value : `${value.slice(0, max)}…`;
4462
4462
  }
4463
+ function transportErrorMessage(error) {
4464
+ const messages = [];
4465
+ const seen = new Set;
4466
+ let current = error;
4467
+ while (current && typeof current === "object" && messages.length < 4 && !seen.has(current)) {
4468
+ seen.add(current);
4469
+ const candidate = current;
4470
+ const message = typeof candidate.message === "string" ? truncate(candidate.message.replace(/\s+/g, " ").trim(), 300) : "";
4471
+ const code = typeof candidate.code === "string" ? candidate.code : "";
4472
+ const detail = `${message}${code && !message.includes(code) ? ` (${code})` : ""}`;
4473
+ if (detail && messages.at(-1) !== detail)
4474
+ messages.push(detail);
4475
+ current = candidate.cause;
4476
+ }
4477
+ return messages.join(": ") || "Unknown network error";
4478
+ }
4463
4479
  function namesKeyScopeRefusal(error) {
4464
4480
  if (typeof error.code === "string" && KEY_SCOPE_REFUSALS.has(error.code))
4465
4481
  return true;
@@ -4683,7 +4699,7 @@ class SimClient {
4683
4699
  if (timeout?.aborted) {
4684
4700
  throw new SimApiError(`${url} did not answer within ${timeoutMs / 1000}s. ${RAISE_TIMEOUT_HINT}`, 0);
4685
4701
  }
4686
- throw new SimApiError(`Could not reach ${this.profile.endpoint}: ${cause.message}`, 0);
4702
+ throw new SimApiError(`Could not reach ${this.profile.endpoint}: ${transportErrorMessage(cause)}`, 0);
4687
4703
  }
4688
4704
  if (trace)
4689
4705
  traceRequest(method, url, response.status, startedAt);
@@ -17239,6 +17255,47 @@ function attachCredentialCommands(program) {
17239
17255
  credentials.command("reconnect").argument("<credentialId>", "Existing OAuth credential to re-authorize").description(describeOperation(V2_OPERATIONS.createCredentialConnection, "Create a short-lived link for reconnecting an OAuth credential")).action((credentialId, _options, command) => createConnectionLink(command, { credentialId }));
17240
17256
  }
17241
17257
 
17258
+ // src/http/ndjson.ts
17259
+ async function* readNdjson(body, protocol) {
17260
+ if (!body) {
17261
+ throw new SimApiError(`${protocol} ended without a response body`, 0);
17262
+ }
17263
+ const reader = body.getReader();
17264
+ const decoder = new TextDecoder;
17265
+ let buffer = "";
17266
+ const parse = (line) => {
17267
+ const trimmed = line.trim();
17268
+ if (!trimmed)
17269
+ return;
17270
+ try {
17271
+ return JSON.parse(trimmed);
17272
+ } catch {
17273
+ throw new SimApiError(`${protocol} returned malformed data`, 0);
17274
+ }
17275
+ };
17276
+ try {
17277
+ while (true) {
17278
+ const { done, value } = await reader.read();
17279
+ buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });
17280
+ const lines = buffer.split(`
17281
+ `);
17282
+ buffer = done ? "" : lines.pop() ?? "";
17283
+ for (const line of lines) {
17284
+ const event = parse(line);
17285
+ if (event !== undefined)
17286
+ yield event;
17287
+ }
17288
+ if (done)
17289
+ return;
17290
+ }
17291
+ } finally {
17292
+ reader.cancel().catch(() => {
17293
+ return;
17294
+ });
17295
+ reader.releaseLock();
17296
+ }
17297
+ }
17298
+
17242
17299
  // src/commands/protocol/result.ts
17243
17300
  function printProtocolResult(format, result) {
17244
17301
  const fields = Object.entries(result).map(([key, value]) => [key, text(value)]);
@@ -17246,72 +17303,28 @@ function printProtocolResult(format, result) {
17246
17303
  }
17247
17304
 
17248
17305
  // src/commands/protocol/chat.ts
17249
- function parseChatStreamLine(line) {
17250
- const trimmed = line.trim();
17251
- if (!trimmed)
17252
- return;
17253
- try {
17254
- return JSON.parse(trimmed);
17255
- } catch {
17256
- throw new SimApiError("Chat stream returned malformed data", 0);
17257
- }
17258
- }
17259
17306
  async function readChatStream(response, onChunk) {
17260
- if (!response.body) {
17261
- throw new SimApiError("Chat stream ended without a response body", 0);
17262
- }
17263
- const reader = response.body.getReader();
17264
- const decoder = new TextDecoder;
17265
- let buffer = "";
17266
- let finalResult;
17267
- const processLine = (line) => {
17268
- const event = parseChatStreamLine(line);
17269
- if (!event || event.type === "heartbeat")
17270
- return false;
17307
+ for await (const value of readNdjson(response.body, "Chat stream")) {
17308
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
17309
+ throw new SimApiError("Chat stream returned an unknown event", 0);
17310
+ }
17311
+ const event = value;
17312
+ if (event.type === "heartbeat")
17313
+ continue;
17271
17314
  if (event.type === "chunk") {
17272
17315
  if (event.content)
17273
17316
  onChunk(sanitize(event.content));
17274
- return false;
17317
+ continue;
17275
17318
  }
17276
17319
  if (event.type === "error") {
17277
17320
  throw new SimApiError(event.error || "Chat request failed", 0);
17278
17321
  }
17279
17322
  if (event.type === "final") {
17280
- finalResult = event.data;
17281
- return true;
17323
+ return event.data;
17282
17324
  }
17283
17325
  throw new SimApiError("Chat stream returned an unknown event", 0);
17284
- };
17285
- try {
17286
- let ended = false;
17287
- while (!ended) {
17288
- const { done, value } = await reader.read();
17289
- if (done)
17290
- break;
17291
- buffer += decoder.decode(value, { stream: true });
17292
- const lines = buffer.split(`
17293
- `);
17294
- buffer = lines.pop() ?? "";
17295
- for (const line of lines) {
17296
- if (processLine(line)) {
17297
- ended = true;
17298
- break;
17299
- }
17300
- }
17301
- }
17302
- if (!ended) {
17303
- buffer += decoder.decode();
17304
- processLine(buffer);
17305
- }
17306
- if (!finalResult) {
17307
- throw new SimApiError("Chat stream ended without a final result", 0);
17308
- }
17309
- return finalResult;
17310
- } finally {
17311
- reader.cancel().catch(() => {
17312
- return;
17313
- });
17314
17326
  }
17327
+ throw new SimApiError("Chat stream ended without a final result", 0);
17315
17328
  }
17316
17329
  function ignoreBrokenPipe(stream) {
17317
17330
  const onError = (error) => {
@@ -18302,6 +18315,7 @@ function attachTableImport(tables) {
18302
18315
  // src/commands/protocol/workflow-run-follow.ts
18303
18316
  var AGENT_STREAM_PROTOCOL_HEADER = "x-sim-stream-protocol";
18304
18317
  var AGENT_STREAM_PROTOCOL_V1 = "agent-events-v1";
18318
+ var WORKFLOW_RESULT_STREAM_CONTENT_TYPE = "application/x-ndjson";
18305
18319
  var DONE_SENTINEL = "[DONE]";
18306
18320
  function resolveWorkflowRunSelection(flags) {
18307
18321
  const manual = flags.manual === true;
@@ -18353,6 +18367,59 @@ function stringField(frame, key) {
18353
18367
  const value = frame[key];
18354
18368
  return typeof value === "string" ? value : null;
18355
18369
  }
18370
+ async function readWorkflowResult(response) {
18371
+ const contentType = (response.headers.get("content-type") ?? "").toLowerCase();
18372
+ if (!contentType.includes(WORKFLOW_RESULT_STREAM_CONTENT_TYPE)) {
18373
+ let envelope;
18374
+ try {
18375
+ envelope = await response.json();
18376
+ } catch {
18377
+ throw new SimApiError(`Workflow run returned malformed JSON${contentType ? ` as ${contentType}` : ""}`, response.status);
18378
+ }
18379
+ if (!isRecord(envelope)) {
18380
+ throw new SimApiError("Workflow run returned an invalid result envelope", response.status);
18381
+ }
18382
+ return isRecord(envelope.data) ? envelope.data : envelope;
18383
+ }
18384
+ for await (const value of readNdjson(response.body, "Workflow result stream")) {
18385
+ if (!isRecord(value) || typeof value.type !== "string") {
18386
+ throw new SimApiError("Workflow result stream returned an unknown event", response.status);
18387
+ }
18388
+ if (value.type === "heartbeat")
18389
+ continue;
18390
+ if (value.type === "error") {
18391
+ throw new SimApiError(safeOneLine(typeof value.error === "string" ? value.error : "Workflow run failed"), typeof value.status === "number" ? value.status : 0, typeof value.code === "string" ? value.code : null);
18392
+ }
18393
+ if (value.type === "final" && isRecord(value.data))
18394
+ return value.data;
18395
+ throw new SimApiError("Workflow result stream returned an unknown event", response.status);
18396
+ }
18397
+ throw new SimApiError("Workflow result stream ended without a final result", response.status);
18398
+ }
18399
+ async function runWithResultStream(workflowId, command) {
18400
+ const flags = command.optsWithGlobals();
18401
+ const { client, profile } = clientFrom(command);
18402
+ const operation = V2_OPERATIONS.executeWorkflow;
18403
+ const commandSpec = CLI_CONTRACT.executeWorkflow ?? {};
18404
+ try {
18405
+ const request = buildRequest("executeWorkflow", [workflowId], flags, profile.workspaceId);
18406
+ const response = await client.requestRaw(request.path, {
18407
+ method: operation.method,
18408
+ query: request.query,
18409
+ body: request.body,
18410
+ headers: { ...request.headers, accept: WORKFLOW_RESULT_STREAM_CONTENT_TYPE }
18411
+ });
18412
+ const payload = await readWorkflowResult(response);
18413
+ renderResult("executeWorkflow", profile.output, payload, commandSpec, {
18414
+ expandedTrace: flags.trace === true
18415
+ });
18416
+ const failure = runFailureMessage("executeWorkflow", payload);
18417
+ if (failure)
18418
+ throw new SimApiError(failure, 0);
18419
+ } catch (error) {
18420
+ throw retypeApiError(error, "executeWorkflow", commandSpec, operation);
18421
+ }
18422
+ }
18356
18423
  async function* sseData(body) {
18357
18424
  const reader = body.getReader();
18358
18425
  const decoder = new TextDecoder;
@@ -18524,6 +18591,10 @@ function followOrDelegate(previous) {
18524
18591
  if (flags.includeThinking === true || flags.includeToolCalls === true) {
18525
18592
  throw new SimApiError("--include-thinking and --include-tool-calls describe a stream; add --follow", 0);
18526
18593
  }
18594
+ if (flags.async !== true) {
18595
+ await runWithResultStream(workflowId, command);
18596
+ return;
18597
+ }
18527
18598
  if (previous) {
18528
18599
  await previous(command.processedArgs);
18529
18600
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sim",
3
- "version": "2.1.8-preview.98.1",
3
+ "version": "2.1.8-preview.99.1",
4
4
  "description": "Sim CLI - talk to the Sim API from your terminal",
5
5
  "type": "module",
6
6
  "bin": {