@mindstudio-ai/agent 0.1.47 → 0.1.49

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/index.d.ts CHANGED
@@ -656,12 +656,13 @@ interface TableConfig {
656
656
  defaults?: Record<string, unknown>;
657
657
  /**
658
658
  * Platform-managed auth columns. Set when this table is the app's auth
659
- * table. Writes to email/phone columns throw; roles writes are allowed.
659
+ * table. Writes to email/phone/apiKey columns throw; roles writes are allowed.
660
660
  */
661
661
  managedColumns?: {
662
662
  email?: string;
663
663
  phone?: string;
664
664
  roles?: string;
665
+ apiKey?: string;
665
666
  };
666
667
  /**
667
668
  * Sync role changes to the platform after a successful auth table write.
@@ -956,7 +957,7 @@ declare class Table<T> {
956
957
  * @param data - Row data to insert (or update on conflict). Defaults apply.
957
958
  */
958
959
  upsert(conflictKey: (keyof Omit<T, SystemFields> & string) | (keyof Omit<T, SystemFields> & string)[], data: PushInput<T>): Mutation<T>;
959
- /** @internal Throw if data includes a platform-managed email/phone column. */
960
+ /** @internal Throw if data includes a platform-managed read-only column. */
960
961
  private _checkManagedColumns;
961
962
  /**
962
963
  * @internal Fire role sync for rows that wrote to the roles column.
@@ -1212,12 +1213,16 @@ interface RunTaskResult<T = unknown> {
1212
1213
  /**
1213
1214
  * Client for the MindStudio direct step execution API.
1214
1215
  *
1215
- * Create an instance and call typed step methods directly:
1216
+ * Inside MindStudio apps, use the `mindstudio` singleton (auth is automatic):
1217
+ * ```ts
1218
+ * import { mindstudio } from '@mindstudio-ai/agent';
1219
+ * const { imageUrl } = await mindstudio.generateImage({ prompt: "a sunset" });
1220
+ * ```
1216
1221
  *
1222
+ * For external usage with an API key:
1217
1223
  * ```ts
1218
1224
  * const agent = new MindStudioAgent({ apiKey: "your-key" });
1219
- * const { imageUrl } = await agent.generateImage({ prompt: "a sunset", mode: "background" });
1220
- * console.log(imageUrl);
1225
+ * const { imageUrl } = await agent.generateImage({ prompt: "a sunset" });
1221
1226
  * ```
1222
1227
  *
1223
1228
  * Authentication is resolved in order:
@@ -6034,10 +6039,11 @@ interface StepMethods {
6034
6039
  */
6035
6040
  extractAudioFromVideo(step: ExtractAudioFromVideoStepInput, options?: StepExecutionOptions): Promise<StepExecutionResult<ExtractAudioFromVideoStepOutput>>;
6036
6041
  /**
6037
- * Download a file from a URL and extract its text content. Supports PDFs, plain text files, and other document formats.
6042
+ * Download a file from a URL and extract its text content. Supports PDFs (including scanned/image-based PDFs via OCR), plain text files, and other document formats.
6038
6043
  *
6039
6044
  * @remarks
6040
6045
  * - Best suited for PDFs and raw text/document files. For web pages, use the scrapeUrl step instead.
6046
+ * - Handles both text-layer PDFs and image-based/scanned PDFs (e.g. Figma/Canva exports, scanned documents). Image-based PDFs are processed with OCR automatically — there is no need to convert PDF pages to images first.
6041
6047
  * - Accepts a single URL, a comma-separated list of URLs, or a JSON array of URLs.
6042
6048
  * - Files are rehosted on the MindStudio CDN before extraction.
6043
6049
  * - Maximum file size is 50MB per URL.
package/dist/index.js CHANGED
@@ -1493,15 +1493,16 @@ var Table = class {
1493
1493
  // -------------------------------------------------------------------------
1494
1494
  // Internal helpers
1495
1495
  // -------------------------------------------------------------------------
1496
- /** @internal Throw if data includes a platform-managed email/phone column. */
1496
+ /** @internal Throw if data includes a platform-managed read-only column. */
1497
1497
  _checkManagedColumns(data) {
1498
1498
  const mc = this._config.managedColumns;
1499
1499
  if (!mc) return;
1500
1500
  const keys = Object.keys(data);
1501
1501
  for (const key of keys) {
1502
- if (mc.email && key === mc.email || mc.phone && key === mc.phone) {
1502
+ if (mc.email && key === mc.email || mc.phone && key === mc.phone || mc.apiKey && key === mc.apiKey) {
1503
+ const friendly = key === mc.email ? "email" : key === mc.phone ? "phone" : "API key";
1503
1504
  throw new MindStudioError(
1504
- `Cannot write to "${key}" \u2014 this column is managed by auth. Use the auth API to change a user's ${key === mc.email ? "email" : "phone"}.`,
1505
+ `Cannot write to "${key}" \u2014 this column is managed by auth. Use the auth API to change a user's ${friendly}.`,
1505
1506
  "managed_column_write",
1506
1507
  400
1507
1508
  );
@@ -1958,8 +1959,8 @@ var stepMetadata = {
1958
1959
  },
1959
1960
  "extractText": {
1960
1961
  stepType: "extractText",
1961
- description: "Download a file from a URL and extract its text content. Supports PDFs, plain text files, and other document formats.",
1962
- usageNotes: "- Best suited for PDFs and raw text/document files. For web pages, use the scrapeUrl step instead.\n- Accepts a single URL, a comma-separated list of URLs, or a JSON array of URLs.\n- Files are rehosted on the MindStudio CDN before extraction.\n- Maximum file size is 50MB per URL.",
1962
+ description: "Download a file from a URL and extract its text content. Supports PDFs (including scanned/image-based PDFs via OCR), plain text files, and other document formats.",
1963
+ usageNotes: "- Best suited for PDFs and raw text/document files. For web pages, use the scrapeUrl step instead.\n- Handles both text-layer PDFs and image-based/scanned PDFs (e.g. Figma/Canva exports, scanned documents). Image-based PDFs are processed with OCR automatically \u2014 there is no need to convert PDF pages to images first.\n- Accepts a single URL, a comma-separated list of URLs, or a JSON array of URLs.\n- Files are rehosted on the MindStudio CDN before extraction.\n- Maximum file size is 50MB per URL.",
1963
1964
  inputSchema: { "type": "object", "properties": { "url": { "anyOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] } }, "required": ["url"] },
1964
1965
  outputSchema: { "type": "object", "properties": { "text": { "anyOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] } }, "required": ["text"] }
1965
1966
  },
@@ -3672,48 +3673,80 @@ var MindStudioAgent = class {
3672
3673
  );
3673
3674
  }
3674
3675
  const threadId = options?.threadId ?? (this._reuseThreadId && !getRequestContext() ? this._threadId : void 0);
3675
- const { data, headers } = await request(this._httpConfig, "POST", `/steps/${stepType}/execute`, {
3676
+ const { data: asyncData, headers } = await request(this._currentHttpConfig, "POST", `/steps/${stepType}/execute-async`, {
3676
3677
  step,
3677
3678
  ...options?.appId != null && { appId: options.appId },
3678
3679
  ...threadId != null && { threadId },
3679
3680
  ...this._currentStreamId != null && { streamId: this._currentStreamId }
3680
3681
  });
3681
- let output;
3682
- if (data.output != null) {
3683
- output = data.output;
3684
- } else if (data.outputUrl) {
3685
- const res = await fetch(data.outputUrl);
3682
+ const remaining = headers.get("x-ratelimit-remaining");
3683
+ const returnedThreadId = asyncData.threadId ?? "";
3684
+ if (this._reuseThreadId && returnedThreadId && !getRequestContext()) {
3685
+ this._threadId = returnedThreadId;
3686
+ }
3687
+ if (!this._appId && asyncData.appId && !getRequestContext()) {
3688
+ this._appId = asyncData.appId;
3689
+ }
3690
+ const pollUrl = `${this._currentHttpConfig.baseUrl}/developer/v2/steps/${stepType}/execute-async/poll/${asyncData.executionToken}`;
3691
+ let pollDelay = 100;
3692
+ while (true) {
3693
+ await sleep3(pollDelay);
3694
+ pollDelay = Math.min(pollDelay * 2, 5e3);
3695
+ const res = await fetch(pollUrl, {
3696
+ headers: { "User-Agent": "@mindstudio-ai/agent" }
3697
+ });
3698
+ if (res.status === 502 || res.status === 503 || res.status === 504) continue;
3699
+ if (res.status === 404) {
3700
+ throw new MindStudioError(
3701
+ `[${stepType}] Execution token expired.`,
3702
+ "poll_token_expired",
3703
+ 404
3704
+ );
3705
+ }
3686
3706
  if (!res.ok) {
3707
+ const errorBody = await res.json().catch(() => ({}));
3687
3708
  throw new MindStudioError(
3688
- `Failed to fetch ${stepType} output from S3: ${res.status} ${res.statusText}`,
3689
- "output_fetch_error",
3690
- res.status
3709
+ errorBody.message ?? errorBody.error ?? `[${stepType}] Poll failed: ${res.status} ${res.statusText}`,
3710
+ errorBody.code ?? "poll_error",
3711
+ res.status,
3712
+ errorBody
3691
3713
  );
3692
3714
  }
3693
- const envelope = await res.json();
3694
- output = envelope.value;
3695
- } else {
3696
- output = void 0;
3697
- }
3698
- const returnedThreadId = headers.get("x-mindstudio-thread-id") ?? "";
3699
- if (this._reuseThreadId && returnedThreadId && !getRequestContext()) {
3700
- this._threadId = returnedThreadId;
3701
- }
3702
- const returnedAppId = headers.get("x-mindstudio-app-id");
3703
- if (!this._appId && returnedAppId && !getRequestContext()) {
3704
- this._appId = returnedAppId;
3715
+ const poll = await res.json();
3716
+ if (poll.status === "pending") continue;
3717
+ if (poll.status === "error") {
3718
+ throw new MindStudioError(
3719
+ `[${stepType}] ${poll.error ?? "Step execution failed."}`,
3720
+ "step_error",
3721
+ 500
3722
+ );
3723
+ }
3724
+ let output;
3725
+ if (poll.output != null) {
3726
+ output = poll.output;
3727
+ } else if (poll.outputUrl) {
3728
+ const s3Res = await fetch(poll.outputUrl);
3729
+ if (!s3Res.ok) {
3730
+ throw new MindStudioError(
3731
+ `Failed to fetch ${stepType} output from S3: ${s3Res.status} ${s3Res.statusText}`,
3732
+ "output_fetch_error",
3733
+ s3Res.status
3734
+ );
3735
+ }
3736
+ const envelope = await s3Res.json();
3737
+ output = envelope.value;
3738
+ } else {
3739
+ output = void 0;
3740
+ }
3741
+ return {
3742
+ ...output,
3743
+ $appId: poll.appId ?? asyncData.appId ?? "",
3744
+ $threadId: poll.threadId ?? returnedThreadId,
3745
+ $rateLimitRemaining: remaining != null ? parseInt(remaining, 10) : void 0,
3746
+ $billingCost: poll.billingCost,
3747
+ $billingEvents: poll.billingEvents
3748
+ };
3705
3749
  }
3706
- const remaining = headers.get("x-ratelimit-remaining");
3707
- const billingCost = headers.get("x-mindstudio-billing-cost");
3708
- const billingEvents = headers.get("x-mindstudio-billing-events");
3709
- return {
3710
- ...output,
3711
- $appId: headers.get("x-mindstudio-app-id") ?? "",
3712
- $threadId: returnedThreadId,
3713
- $rateLimitRemaining: remaining != null ? parseInt(remaining, 10) : void 0,
3714
- $billingCost: billingCost != null ? parseFloat(billingCost) : void 0,
3715
- $billingEvents: billingEvents != null ? JSON.parse(billingEvents) : void 0
3716
- };
3717
3750
  }
3718
3751
  /**
3719
3752
  * @internal Streaming step execution — sends `Accept: text/event-stream`