@overaihq/cli 0.14.11 → 0.14.13

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 (3) hide show
  1. package/README.md +6 -0
  2. package/dist/index.js +1286 -823
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -52345,6 +52345,317 @@ var exchangeSessionToken = (baseUrl, refreshToken, destinationOrgId) => unauthen
52345
52345
  destinationOrgId
52346
52346
  });
52347
52347
 
52348
+ // ../sdk/src/error.ts
52349
+ var OverAIError = class extends Error {
52350
+ /** HTTP status; 200 for a GraphQL errors-array failure. */
52351
+ status;
52352
+ /** URL pathname of the request, "" when unknown. */
52353
+ path;
52354
+ /** Parsed JSON body, the raw string when not JSON, or null. */
52355
+ body;
52356
+ /** The org the request ran as, when known. */
52357
+ organizationId;
52358
+ /** GraphQL errors array, when that is the cause. */
52359
+ errors;
52360
+ constructor(message, fields) {
52361
+ super(message);
52362
+ this.status = fields.status;
52363
+ this.path = fields.path;
52364
+ this.body = fields.body;
52365
+ this.organizationId = fields.organizationId;
52366
+ if (fields.errors != null) this.errors = fields.errors;
52367
+ }
52368
+ };
52369
+ var responsePath = (res) => {
52370
+ try {
52371
+ return new URL(res.url).pathname;
52372
+ } catch {
52373
+ return "";
52374
+ }
52375
+ };
52376
+ var STATUS_TEXT = {
52377
+ 400: "Bad Request",
52378
+ 401: "Unauthorized",
52379
+ 403: "Forbidden",
52380
+ 404: "Not Found",
52381
+ 409: "Conflict",
52382
+ 413: "Payload Too Large",
52383
+ 429: "Too Many Requests",
52384
+ 500: "Internal Server Error",
52385
+ 503: "Service Unavailable"
52386
+ };
52387
+ var apiError = (label, res, rawBody, organizationId) => {
52388
+ const body = (() => {
52389
+ try {
52390
+ return JSON.parse(rawBody);
52391
+ } catch {
52392
+ return rawBody;
52393
+ }
52394
+ })();
52395
+ const structured = typeof body === "object" && body != null ? body : null;
52396
+ const field = structured?.error ?? structured?.message;
52397
+ const claimed = (typeof field === "string" ? field : rawBody).trim().slice(0, 500);
52398
+ const reason = claimed.length === 0 || claimed === "Error" || claimed === "null" || claimed === "undefined" || claimed.toLowerCase() === STATUS_TEXT[res.status]?.toLowerCase() ? null : claimed;
52399
+ const path = responsePath(res);
52400
+ const fields = { status: res.status, path, body, organizationId };
52401
+ if (reason != null) return new OverAIError(reason, fields);
52402
+ return new OverAIError(
52403
+ `${label}: HTTP ${String(res.status)}, no reason given by the server.${path === "" ? "" : `
52404
+ ${path}`}${organizationId == null ? "" : `
52405
+ Asked as org ${organizationId}.`}`,
52406
+ fields
52407
+ );
52408
+ };
52409
+
52410
+ // ../sdk/src/agentRun.ts
52411
+ var agentRun = (client, id) => {
52412
+ const deployedVersionNumber = async () => {
52413
+ const deployed = await client.graphql(
52414
+ `query SdkAgentDeployedVersion($id: String!) {
52415
+ agentDeployedVersions(condition: { agentId: $id }) { nodes { versionNumber } }
52416
+ }`,
52417
+ { id }
52418
+ );
52419
+ const versionNumber = deployed.agentDeployedVersions?.nodes[0]?.versionNumber;
52420
+ if (versionNumber == null) {
52421
+ throw new Error(
52422
+ `Agent ${id} has no deployed version; pass --version <n> to inspect a saved one.`
52423
+ );
52424
+ }
52425
+ return versionNumber;
52426
+ };
52427
+ return {
52428
+ generate: async (message) => {
52429
+ const organizationId = await client.organizationId();
52430
+ const res = await client.fetch(
52431
+ `/v2/agents/${organizationId}/${id}/generate/text`,
52432
+ {
52433
+ method: "POST",
52434
+ headers: { "Content-Type": "application/json" },
52435
+ body: JSON.stringify({ message })
52436
+ }
52437
+ );
52438
+ const raw2 = await res.text();
52439
+ if (!res.ok) throw apiError("agent generate", res, raw2, organizationId);
52440
+ return JSON.parse(raw2);
52441
+ },
52442
+ /*
52443
+ * What an agent can actually do is spread over five bindings and a model,
52444
+ * and nothing in the product shows it in one place.
52445
+ */
52446
+ tools: async (ref = "deployed") => {
52447
+ const version2 = typeof ref === "number" ? ref : ref === "latest" ? (await client.agent(id).head()).latestVersion : await deployedVersionNumber();
52448
+ const data = await client.graphql(
52449
+ `query SdkAgentTools($id: String!, $version: Int!) {
52450
+ agent(id: $id) {
52451
+ name
52452
+ agentVersions(first: 1, condition: { versionNumber: $version }) {
52453
+ nodes {
52454
+ versionNumber
52455
+ model
52456
+ agentVersionFunctionsByAgentIdAndVersionNumber(orderBy: [FUNCTION_ID_ASC]) {
52457
+ nodes { functionId function { name } }
52458
+ }
52459
+ agentVersionTableSpacesByAgentIdAndVersionNumber(orderBy: [TABLE_SPACE_ID_ASC]) {
52460
+ nodes { tableSpaceId accessMode }
52461
+ }
52462
+ agentVersionAgentsByAgentIdAndVersionNumber(orderBy: [ATTACHED_AGENT_ID_ASC]) {
52463
+ nodes { attachedAgentId }
52464
+ }
52465
+ agentVersionKnowledgeBasesByAgentIdAndVersionNumber(orderBy: [KNOWLEDGE_BASE_ID_ASC]) {
52466
+ nodes { knowledgeBaseId }
52467
+ }
52468
+ agentVersionBuiltinToolSetsByAgentIdAndVersionNumber(orderBy: [TOOL_SET_ASC]) {
52469
+ nodes { toolSet }
52470
+ }
52471
+ }
52472
+ }
52473
+ }
52474
+ }`,
52475
+ { id, version: version2 }
52476
+ );
52477
+ if (data.agent == null) throw new Error(`Agent ${id} not found.`);
52478
+ const agentVersion = data.agent.agentVersions.nodes[0];
52479
+ if (agentVersion == null) {
52480
+ throw new Error(`Agent ${id} has no version ${String(version2)}.`);
52481
+ }
52482
+ return {
52483
+ agent: data.agent.name,
52484
+ version: agentVersion.versionNumber,
52485
+ model: agentVersion.model,
52486
+ tools: [
52487
+ ...agentVersion.agentVersionFunctionsByAgentIdAndVersionNumber.nodes.map(
52488
+ (link) => ({
52489
+ kind: "function",
52490
+ name: link.function?.name ?? link.functionId,
52491
+ detail: link.functionId
52492
+ })
52493
+ ),
52494
+ ...agentVersion.agentVersionTableSpacesByAgentIdAndVersionNumber.nodes.map(
52495
+ (link) => ({
52496
+ kind: "table space",
52497
+ name: link.tableSpaceId,
52498
+ detail: link.accessMode
52499
+ })
52500
+ ),
52501
+ ...agentVersion.agentVersionAgentsByAgentIdAndVersionNumber.nodes.map(
52502
+ (link) => ({
52503
+ kind: "sub-agent",
52504
+ name: link.attachedAgentId,
52505
+ detail: ""
52506
+ })
52507
+ ),
52508
+ ...agentVersion.agentVersionKnowledgeBasesByAgentIdAndVersionNumber.nodes.map(
52509
+ (link) => ({
52510
+ kind: "knowledge base",
52511
+ name: link.knowledgeBaseId,
52512
+ detail: ""
52513
+ })
52514
+ ),
52515
+ ...agentVersion.agentVersionBuiltinToolSetsByAgentIdAndVersionNumber.nodes.map(
52516
+ (link) => ({
52517
+ kind: "built-in",
52518
+ name: link.toolSet,
52519
+ detail: ""
52520
+ })
52521
+ )
52522
+ ]
52523
+ };
52524
+ },
52525
+ /* A caller that wants a second turn must create the conversation first — as the webapp does. */
52526
+ createConversation: async (title) => {
52527
+ const versionNumber = await deployedVersionNumber();
52528
+ const created = await client.graphql(
52529
+ `mutation SdkCreateAgentConversation($input: CreateAgentConversationInput!) {
52530
+ createAgentConversation(input: $input) { agentConversation { id } }
52531
+ }`,
52532
+ {
52533
+ input: {
52534
+ agentConversation: {
52535
+ agentId: id,
52536
+ versionNumber,
52537
+ title: title.length > 50 ? `${title.slice(0, 47)}...` : title
52538
+ }
52539
+ }
52540
+ }
52541
+ );
52542
+ const conversationId = created.createAgentConversation?.agentConversation?.id;
52543
+ if (conversationId == null)
52544
+ throw new Error("Failed to create a conversation.");
52545
+ return conversationId;
52546
+ },
52547
+ chat: async (messages, options) => {
52548
+ const organizationId = await client.organizationId();
52549
+ const res = await client.fetch(`/v2/agents/${organizationId}/${id}`, {
52550
+ method: "POST",
52551
+ headers: { "Content-Type": "application/json" },
52552
+ body: JSON.stringify({
52553
+ messages,
52554
+ conversationId: options.conversationId
52555
+ })
52556
+ });
52557
+ if (!res.ok)
52558
+ throw apiError("agent chat", res, await res.text(), organizationId);
52559
+ return res;
52560
+ }
52561
+ };
52562
+ };
52563
+ var agentStreamEvents = (response) => {
52564
+ const parse4 = (line) => {
52565
+ if (!line.startsWith("data: ")) return null;
52566
+ const payload = line.slice(6);
52567
+ if (payload === "[DONE]") return "done";
52568
+ const event = (() => {
52569
+ try {
52570
+ return JSON.parse(payload);
52571
+ } catch {
52572
+ return null;
52573
+ }
52574
+ })();
52575
+ if (event?.type === "text-delta" && event.delta != null)
52576
+ return { type: "text-delta", delta: event.delta };
52577
+ if (event?.type === "error")
52578
+ return event.errorText == null ? { type: "error" } : { type: "error", errorText: event.errorText };
52579
+ return null;
52580
+ };
52581
+ return (async function* () {
52582
+ const body = response.body;
52583
+ if (body == null) return;
52584
+ const decoder = new TextDecoder();
52585
+ const reader = body.getReader();
52586
+ let buffer = "";
52587
+ for (; ; ) {
52588
+ const { value, done } = await reader.read();
52589
+ if (done) break;
52590
+ buffer += decoder.decode(value, { stream: true });
52591
+ const lines = buffer.split("\n");
52592
+ buffer = lines.pop() ?? "";
52593
+ for (const line of lines) {
52594
+ const event = parse4(line);
52595
+ if (event === "done") return;
52596
+ if (event != null) yield event;
52597
+ }
52598
+ }
52599
+ const last2 = parse4(buffer + decoder.decode());
52600
+ if (last2 != null && last2 !== "done") yield last2;
52601
+ })();
52602
+ };
52603
+
52604
+ // ../sdk/src/automationRun.ts
52605
+ var automationRunApi = (client, id) => ({
52606
+ run: async (inputs) => {
52607
+ const organizationId = await client.organizationId();
52608
+ const res = await client.fetch(
52609
+ `/v2/automations/${organizationId}/${id}/run`,
52610
+ {
52611
+ method: "POST",
52612
+ headers: { "Content-Type": "application/json" },
52613
+ body: JSON.stringify(inputs ?? {})
52614
+ }
52615
+ );
52616
+ const raw2 = await res.text();
52617
+ if (!res.ok) throw apiError("automation run", res, raw2, organizationId);
52618
+ return JSON.parse(raw2);
52619
+ },
52620
+ /*
52621
+ * The v2 condition filters only by automationId; status filters apply
52622
+ * here (the condition type has no errored/isCancelled fields), so a
52623
+ * filtered read over-fetches to still fill the limit.
52624
+ */
52625
+ runs: async (options) => {
52626
+ const limit2 = options?.limit ?? 20;
52627
+ const filtered = options?.failed === true || options?.running === true;
52628
+ const data = await client.graphql(
52629
+ `query SdkAutomationRuns($automationId: String!, $first: Int!) {
52630
+ automationRuns(condition: { automationId: $automationId }, orderBy: [STARTED_AT_DESC], first: $first) {
52631
+ nodes { id startedAt endedAt errored isCancelled manuallyTriggered version { num } }
52632
+ }
52633
+ }`,
52634
+ { automationId: id, first: filtered ? limit2 * 5 : limit2 }
52635
+ );
52636
+ return data.automationRuns.nodes.filter((r) => options?.failed !== true || r.errored).filter(
52637
+ (r) => options?.running !== true || r.endedAt == null && !r.isCancelled
52638
+ ).slice(0, limit2).map((r) => ({
52639
+ id: r.id,
52640
+ version: r.version?.num,
52641
+ startedAt: r.startedAt,
52642
+ endedAt: r.endedAt,
52643
+ status: r.errored ? "errored" : r.isCancelled ? "cancelled" : r.endedAt == null ? "running" : "ok",
52644
+ manual: r.manuallyTriggered
52645
+ }));
52646
+ }
52647
+ });
52648
+ var automationRunState = async (client, runId) => {
52649
+ const data = await client.graphql(
52650
+ `query SdkAutomationRunState($id: String!) {
52651
+ automationRun(id: $id) { state }
52652
+ }`,
52653
+ { id: runId }
52654
+ );
52655
+ if (data.automationRun == null) throw new Error(`Run ${runId} not found.`);
52656
+ return data.automationRun.state ?? {};
52657
+ };
52658
+
52348
52659
  // ../common/src/agent.ts
52349
52660
  var agentVersionConfigSchema = external_exports.object({
52350
52661
  systemPrompt: external_exports.string(),
@@ -58547,6 +58858,81 @@ var createDataFile = async (client, input2) => {
58547
58858
  return { id };
58548
58859
  };
58549
58860
 
58861
+ // ../sdk/src/dataFiles.ts
58862
+ var dataFileResource = (client, id) => ({
58863
+ read: async () => {
58864
+ const organizationId = await client.organizationId();
58865
+ const res = await client.fetch(`/v2/data-files/${organizationId}/${id}`, {
58866
+ redirect: "follow"
58867
+ });
58868
+ if (!res.ok)
58869
+ throw apiError(
58870
+ "data-file download",
58871
+ res,
58872
+ await res.text(),
58873
+ organizationId
58874
+ );
58875
+ return res;
58876
+ },
58877
+ head: async (options) => {
58878
+ const organizationId = await client.organizationId();
58879
+ const res = await client.fetch(
58880
+ `/v2/data-files/${organizationId}/${id}/head?numLines=${String(options?.lines ?? 50)}`,
58881
+ { redirect: "follow" }
58882
+ );
58883
+ if (!res.ok)
58884
+ throw apiError("data-file head", res, await res.text(), organizationId);
58885
+ return res;
58886
+ },
58887
+ upload: async (content, options) => {
58888
+ const url2 = await client.trpc("dataFiles.getDataFileUploadUrl", {
58889
+ dataFileId: id,
58890
+ fileSize: content.size,
58891
+ message: options?.message ?? ""
58892
+ });
58893
+ const res = await fetch(url2, { method: "PUT", body: content });
58894
+ if (!res.ok)
58895
+ throw apiError(
58896
+ "data-file upload",
58897
+ res,
58898
+ await res.text(),
58899
+ await client.organizationId()
58900
+ );
58901
+ }
58902
+ });
58903
+
58904
+ // ../sdk/src/dataStreams.ts
58905
+ var dataStreamResource = (client, id) => ({
58906
+ append: async (records) => {
58907
+ const organizationId = await client.organizationId();
58908
+ const res = await client.fetch(`/v1/data-streams/${organizationId}/${id}`, {
58909
+ method: "POST",
58910
+ headers: { "Content-Type": "application/json" },
58911
+ body: JSON.stringify({
58912
+ data: Array.isArray(records) ? records : [records]
58913
+ })
58914
+ });
58915
+ const raw2 = await res.text();
58916
+ if (!res.ok) throw apiError("data-stream append", res, raw2, organizationId);
58917
+ return JSON.parse(raw2);
58918
+ },
58919
+ query: async (options) => {
58920
+ const organizationId = await client.organizationId();
58921
+ const search2 = new URLSearchParams({
58922
+ startAt: options.startAt,
58923
+ endAt: options.endAt,
58924
+ query: options.query ?? ""
58925
+ });
58926
+ if (options.limit != null) search2.set("limit", String(options.limit));
58927
+ const res = await client.fetch(
58928
+ `/v1/data-streams/${organizationId}/${id}?${search2.toString()}`
58929
+ );
58930
+ const raw2 = await res.text();
58931
+ if (!res.ok) throw apiError("data-stream query", res, raw2, organizationId);
58932
+ return JSON.parse(raw2);
58933
+ }
58934
+ });
58935
+
58550
58936
  // ../../node_modules/.pnpm/diff@9.0.0/node_modules/diff/libesm/diff/base.js
58551
58937
  var Diff = class {
58552
58938
  diff(oldStr, newStr, options = {}) {
@@ -60392,67 +60778,106 @@ var functionResource = (client, id) => {
60392
60778
  };
60393
60779
  };
60394
60780
 
60395
- // ../sdk/src/error.ts
60396
- var OverAIError = class extends Error {
60397
- /** HTTP status; 200 for a GraphQL errors-array failure. */
60398
- status;
60399
- /** URL pathname of the request, "" when unknown. */
60400
- path;
60401
- /** Parsed JSON body, the raw string when not JSON, or null. */
60402
- body;
60403
- /** The org the request ran as, when known. */
60404
- organizationId;
60405
- /** GraphQL errors array, when that is the cause. */
60406
- errors;
60407
- constructor(message, fields) {
60408
- super(message);
60409
- this.status = fields.status;
60410
- this.path = fields.path;
60411
- this.body = fields.body;
60412
- this.organizationId = fields.organizationId;
60413
- if (fields.errors != null) this.errors = fields.errors;
60414
- }
60415
- };
60416
- var responsePath = (res) => {
60417
- try {
60418
- return new URL(res.url).pathname;
60419
- } catch {
60420
- return "";
60781
+ // ../sdk/src/functionRun.ts
60782
+ var functionRun = (client, id) => ({
60783
+ /*
60784
+ * Mirrors `env.FNxxx.call` (common-server envProxy `__callFunction`), so a
60785
+ * caller moving between the sandbox and the SDK keeps one mental model.
60786
+ */
60787
+ call: async (init) => {
60788
+ const { path: rawPath = "", query = {}, body, ...rest } = init ?? {};
60789
+ const queryStart = rawPath.indexOf("?");
60790
+ const pathOnly = queryStart === -1 ? rawPath : rawPath.slice(0, queryStart);
60791
+ const subPath = pathOnly === "" || pathOnly.startsWith("/") ? pathOnly : `/${pathOnly}`;
60792
+ const params = new URLSearchParams(
60793
+ queryStart === -1 ? "" : rawPath.slice(queryStart + 1)
60794
+ );
60795
+ Object.entries(query).forEach(([name2, value]) => {
60796
+ params.set(name2, value);
60797
+ });
60798
+ const search2 = params.toString();
60799
+ const headers = new Headers(rest.headers);
60800
+ const isNativeBody = typeof body === "string" || body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams || body instanceof ArrayBuffer || ArrayBuffer.isView(body) || body instanceof ReadableStream;
60801
+ if (body != null && !isNativeBody && !headers.has("Content-Type"))
60802
+ headers.set("Content-Type", "application/json");
60803
+ return client.fetch(
60804
+ `/v2/functions/${await client.organizationId()}/${id}${subPath}${search2 === "" ? "" : `?${search2}`}`,
60805
+ {
60806
+ ...rest,
60807
+ method: rest.method ?? "POST",
60808
+ headers,
60809
+ body: body == null ? void 0 : isNativeBody ? body : JSON.stringify(body)
60810
+ }
60811
+ );
60812
+ },
60813
+ executions: async (options) => (await client.graphql(
60814
+ `query SdkFunctionExecutions($functionId: String!, $first: Int!) {
60815
+ functionExecutions(condition: { functionId: $functionId }, orderBy: [CREATED_AT_DESC], first: $first) {
60816
+ nodes { id createdAt httpStatusCode executionTime requestMethod requestPath versionNumber }
60817
+ }
60818
+ }`,
60819
+ { functionId: id, first: options?.limit ?? 20 }
60820
+ )).functionExecutions.nodes,
60821
+ // Logs and bodies live behind presigned S3 URLs (valid ~1h); they carry
60822
+ // their own auth, so they go through the global fetch with no token.
60823
+ execution: async (executionId) => {
60824
+ const data = await client.graphql(
60825
+ `query SdkFunctionExecution($functionId: String!, $id: String!) {
60826
+ functionExecutions(condition: { functionId: $functionId, id: $id }, first: 1) {
60827
+ nodes { id summary createdAt httpStatusCode executionTime versionNumber logsUrl inputBodyUrl outputBodyUrl }
60828
+ }
60829
+ }`,
60830
+ { functionId: id, id: executionId }
60831
+ );
60832
+ const execution = data.functionExecutions.nodes[0];
60833
+ if (execution == null)
60834
+ throw new Error(`Execution ${executionId} not found.`);
60835
+ const [logs, input2, output2] = await Promise.all(
60836
+ [execution.logsUrl, execution.inputBodyUrl, execution.outputBodyUrl].map(
60837
+ async (url2) => url2 == null ? null : await fetch(url2).then((r) => r.ok ? r.text() : null)
60838
+ )
60839
+ );
60840
+ return {
60841
+ id: execution.id,
60842
+ createdAt: execution.createdAt,
60843
+ httpStatusCode: execution.httpStatusCode,
60844
+ executionTime: execution.executionTime,
60845
+ versionNumber: execution.versionNumber,
60846
+ summary: execution.summary,
60847
+ input: input2 ?? null,
60848
+ output: output2 ?? null,
60849
+ logs: logs ?? null
60850
+ };
60421
60851
  }
60422
- };
60423
- var STATUS_TEXT = {
60424
- 400: "Bad Request",
60425
- 401: "Unauthorized",
60426
- 403: "Forbidden",
60427
- 404: "Not Found",
60428
- 409: "Conflict",
60429
- 413: "Payload Too Large",
60430
- 429: "Too Many Requests",
60431
- 500: "Internal Server Error",
60432
- 503: "Service Unavailable"
60433
- };
60434
- var apiError = (label, res, rawBody, organizationId) => {
60435
- const body = (() => {
60436
- try {
60437
- return JSON.parse(rawBody);
60438
- } catch {
60439
- return rawBody;
60440
- }
60441
- })();
60442
- const structured = typeof body === "object" && body != null ? body : null;
60443
- const field = structured?.error ?? structured?.message;
60444
- const claimed = (typeof field === "string" ? field : rawBody).trim().slice(0, 500);
60445
- const reason = claimed.length === 0 || claimed === "Error" || claimed === "null" || claimed === "undefined" || claimed.toLowerCase() === STATUS_TEXT[res.status]?.toLowerCase() ? null : claimed;
60446
- const path = responsePath(res);
60447
- const fields = { status: res.status, path, body, organizationId };
60448
- if (reason != null) return new OverAIError(reason, fields);
60449
- return new OverAIError(
60450
- `${label}: HTTP ${String(res.status)}, no reason given by the server.${path === "" ? "" : `
60451
- ${path}`}${organizationId == null ? "" : `
60452
- Asked as org ${organizationId}.`}`,
60453
- fields
60454
- );
60455
- };
60852
+ });
60853
+
60854
+ // ../sdk/src/integrations.ts
60855
+ var integrationResource = (client, id) => ({
60856
+ configure: async (value) => {
60857
+ await client.trpc("putIntegrationConfig", { id, value });
60858
+ },
60859
+ // Config reads are for inspection, not secret export; keep credentials out
60860
+ // of terminals, shell history captures, and agent transcripts.
60861
+ config: () => client.trpc("getMaskedIntegrationConfig", { id }),
60862
+ setSecret: async (key, value) => {
60863
+ await client.graphql(
60864
+ `mutation SdkSetIntegrationSecret($input: UpdateIntegrationConfigInput!) {
60865
+ updateIntegrationConfig(input: $input) { success }
60866
+ }`,
60867
+ { input: { integrationId: id, partialConfig: { [key]: value } } }
60868
+ );
60869
+ },
60870
+ // The merge mutation stores a JSON null for `{key: null}`, so removal is its
60871
+ // own mutation server-side.
60872
+ unsetSecret: async (key) => {
60873
+ await client.graphql(
60874
+ `mutation SdkUnsetIntegrationSecret($input: DeleteIntegrationConfigKeyInput!) {
60875
+ deleteIntegrationConfigKey(input: $input) { success }
60876
+ }`,
60877
+ { input: { integrationId: id, key } }
60878
+ );
60879
+ }
60880
+ });
60456
60881
 
60457
60882
  // ../sdk/src/jwt.ts
60458
60883
  var decodeJwtClaims = (token) => {
@@ -60470,6 +60895,112 @@ var decodeJwtClaims = (token) => {
60470
60895
  }
60471
60896
  };
60472
60897
 
60898
+ // ../sdk/src/knowledgeBases.ts
60899
+ var knowledgeBaseResource = (client, id) => ({
60900
+ upload: async (file2, name2) => {
60901
+ const organizationId = await client.organizationId();
60902
+ const formData = new FormData();
60903
+ formData.append("file", file2, name2);
60904
+ const res = await client.fetch(
60905
+ `/v2/knowledge-bases/${organizationId}/${id}/upload`,
60906
+ { method: "POST", body: formData }
60907
+ );
60908
+ const raw2 = await res.text();
60909
+ if (!res.ok)
60910
+ throw apiError("knowledge-base upload", res, raw2, organizationId);
60911
+ return JSON.parse(raw2);
60912
+ },
60913
+ search: async (query, options) => {
60914
+ const organizationId = await client.organizationId();
60915
+ const res = await client.fetch(
60916
+ `/v2/knowledge-bases/${organizationId}/${id}/search`,
60917
+ {
60918
+ method: "POST",
60919
+ headers: { "Content-Type": "application/json" },
60920
+ body: JSON.stringify({
60921
+ query,
60922
+ ...options?.limit != null ? { limit: options.limit } : {}
60923
+ })
60924
+ }
60925
+ );
60926
+ const raw2 = await res.text();
60927
+ if (!res.ok)
60928
+ throw apiError("knowledge-base search", res, raw2, organizationId);
60929
+ return JSON.parse(raw2);
60930
+ }
60931
+ });
60932
+
60933
+ // ../sdk/src/lint.ts
60934
+ var lint = async (client, input2) => {
60935
+ const res = await client.fetch("/v2/architect/lint", {
60936
+ method: "POST",
60937
+ headers: { "Content-Type": "application/json" },
60938
+ body: JSON.stringify(input2)
60939
+ });
60940
+ const raw2 = await res.text();
60941
+ if (!res.ok) throw apiError("lint", res, raw2, await client.organizationId());
60942
+ const parsed = lintResponseSchema.safeParse(
60943
+ (() => {
60944
+ try {
60945
+ return JSON.parse(raw2);
60946
+ } catch {
60947
+ return null;
60948
+ }
60949
+ })()
60950
+ );
60951
+ if (!parsed.success)
60952
+ throw new OverAIError("lint: invalid response from lint service", {
60953
+ status: res.status,
60954
+ path: responsePath(res),
60955
+ body: raw2,
60956
+ organizationId: await client.organizationId()
60957
+ });
60958
+ return parsed.data;
60959
+ };
60960
+
60961
+ // ../sdk/src/printablePageRender.ts
60962
+ var printablePageRender = (client, id) => ({
60963
+ render: async (options) => {
60964
+ const data = await client.graphql(
60965
+ `query SdkPrintablePageWorkspace($id: String!) { printablePage(id: $id) { workspaceId } }`,
60966
+ { id }
60967
+ );
60968
+ if (data.printablePage == null)
60969
+ throw new Error(`Printable page ${id} not found.`);
60970
+ const organizationId = await client.organizationId();
60971
+ const res = await client.fetch(
60972
+ `/v2/actions/${organizationId}/${data.printablePage.workspaceId}/run`,
60973
+ {
60974
+ method: "POST",
60975
+ headers: { "Content-Type": "application/json" },
60976
+ body: JSON.stringify({
60977
+ actionId: "PRINTABLE_PAGE_TO_PDF",
60978
+ inputs: {
60979
+ printablePageId: id,
60980
+ searchParams: options?.searchParams ?? "{}",
60981
+ includeDiagnostics: true
60982
+ }
60983
+ }),
60984
+ signal: AbortSignal.timeout(options?.timeoutMs ?? 6e4)
60985
+ }
60986
+ );
60987
+ const raw2 = await res.text();
60988
+ if (!res.ok)
60989
+ throw apiError("printable-page render", res, raw2, organizationId);
60990
+ return JSON.parse(raw2);
60991
+ }
60992
+ });
60993
+
60994
+ // ../sdk/src/report.ts
60995
+ var report = async (client, kind, details, options) => {
60996
+ await client.trpc("architectIssues.report", {
60997
+ feedbackType: kind,
60998
+ detailedBugReport: details,
60999
+ source: options?.source ?? "cli",
61000
+ cliContext: options?.cliContext
61001
+ });
61002
+ };
61003
+
60473
61004
  // ../common/src/random/index.ts
60474
61005
  var UNMISTAKABLE_CHARS = "0123456789abcdefghjkmnpqrstvwxyz";
60475
61006
  var nanoid4 = customAlphabet(UNMISTAKABLE_CHARS, 12);
@@ -60514,6 +61045,7 @@ var ID_PREFIXES = {
60514
61045
  automation_email_alert: { prefix: "aes", body: 10 },
60515
61046
  automation_error_alert_queue: { prefix: "aeaq", body: 10 },
60516
61047
  billing_period: { prefix: "bpd", body: 12 },
61048
+ billing_term: { prefix: "btm", body: 12 },
60517
61049
  browser_automation: { prefix: "bra", body: 10 },
60518
61050
  browser_automation_run: { prefix: "brr", body: 20 },
60519
61051
  browser_session_usage: { prefix: "bsu", body: 12 },
@@ -60785,6 +61317,301 @@ var genericResource = (client, id) => {
60785
61317
  };
60786
61318
  };
60787
61319
 
61320
+ // ../sdk/src/runners.ts
61321
+ var runnerCollection = (client) => ({
61322
+ /*
61323
+ * Two steps, same as the webapp: createMachineIdentity mints the identity
61324
+ * and binds it to the active org atomically — a plain createIdentity cannot
61325
+ * RETURN a row not yet in the caller's org — then the runner is created in
61326
+ * the workspace.
61327
+ */
61328
+ create: async (input2) => {
61329
+ const identityResult = await client.graphql(
61330
+ `mutation SdkCreateMachineIdentity($name: String!) {
61331
+ createMachineIdentity(input: { name: $name }) {
61332
+ identity { id }
61333
+ }
61334
+ }`,
61335
+ { name: input2.name ?? "Action Runner" }
61336
+ );
61337
+ const identityId = identityResult.createMachineIdentity?.identity?.id;
61338
+ if (identityId == null)
61339
+ throw new Error("Failed to create machine identity");
61340
+ const runnerResult = await client.graphql(
61341
+ `mutation SdkCreateActionRunner($identityId: String!, $workspaceId: String!) {
61342
+ createActionRunner(
61343
+ input: { actionRunner: { identityId: $identityId, workspaceId: $workspaceId } }
61344
+ ) { actionRunner { id } }
61345
+ }`,
61346
+ { identityId, workspaceId: input2.workspaceId }
61347
+ );
61348
+ const actionRunnerId = runnerResult.createActionRunner?.actionRunner?.id;
61349
+ if (actionRunnerId == null)
61350
+ throw new Error("Failed to create action runner");
61351
+ return { actionRunnerId, identityId };
61352
+ },
61353
+ // An explicit null in a PostGraphile condition means IS NULL, so the key is
61354
+ // left out when no workspace is given and the whole org lists.
61355
+ list: async (options) => {
61356
+ const data = await client.graphql(
61357
+ `query SdkRunnerList($condition: ActionRunnerCondition!, $first: Int!) {
61358
+ actionRunners(condition: $condition, first: $first) {
61359
+ nodes { id identity { name } workspaceId isCloud isArchived createdAt }
61360
+ }
61361
+ }`,
61362
+ {
61363
+ condition: options?.workspaceId == null ? {} : { workspaceId: options.workspaceId },
61364
+ first: options?.limit ?? 50
61365
+ }
61366
+ );
61367
+ return data.actionRunners.nodes.map((r) => ({
61368
+ id: r.id,
61369
+ name: r.identity?.name ?? null,
61370
+ workspaceId: r.workspaceId,
61371
+ kind: r.isCloud ? "cloud" : "self-hosted",
61372
+ archived: r.isArchived,
61373
+ createdAt: r.createdAt
61374
+ }));
61375
+ }
61376
+ });
61377
+ var runnerResource = (client, id) => {
61378
+ const enqueueAction = async (actionId, inputs) => {
61379
+ const organizationId = await client.organizationId();
61380
+ const data = await client.graphql(
61381
+ `query SdkResolveRunner($id: String!) {
61382
+ actionRunners(condition: { id: $id }, first: 1) { nodes { workspaceId } }
61383
+ }`,
61384
+ { id }
61385
+ );
61386
+ const workspaceId = data.actionRunners.nodes[0]?.workspaceId;
61387
+ if (workspaceId == null) {
61388
+ throw new Error(
61389
+ `Unknown runner ${id} (or it has no workspace). Try: overai runner list`
61390
+ );
61391
+ }
61392
+ const res = await client.fetch(
61393
+ `/v2/actions/${organizationId}/${workspaceId}/run`,
61394
+ {
61395
+ method: "POST",
61396
+ headers: { "Content-Type": "application/json" },
61397
+ body: JSON.stringify({ actionId, inputs, actionRunnerId: id })
61398
+ }
61399
+ );
61400
+ const raw2 = await res.text();
61401
+ if (!res.ok) throw apiError("runner run", res, raw2, organizationId);
61402
+ return JSON.parse(raw2);
61403
+ };
61404
+ return {
61405
+ token: async (options) => {
61406
+ const result = await client.graphql(
61407
+ `mutation SdkCreateActionRunnerToken($actionRunnerId: String!, $note: String) {
61408
+ createActionRunnerToken(
61409
+ input: { actionRunnerId: $actionRunnerId, note: $note }
61410
+ ) { result }
61411
+ }`,
61412
+ { actionRunnerId: id, note: options?.note ?? "cli" }
61413
+ );
61414
+ const secret = result.createActionRunnerToken?.result;
61415
+ if (secret == null) throw new Error("Failed to mint runner token");
61416
+ return secret;
61417
+ },
61418
+ run: (input2) => enqueueAction("RUNNER_RUN_COMMAND", {
61419
+ actionRunnerId: id,
61420
+ interpreter: input2.interpreter ?? "shell",
61421
+ code: input2.code
61422
+ }),
61423
+ upload: (path) => enqueueAction("RUNNER_UPLOAD_FILE", {
61424
+ actionRunnerId: id,
61425
+ filePath: path
61426
+ })
61427
+ };
61428
+ };
61429
+
61430
+ // ../sdk/src/storage.ts
61431
+ var storageApi = (client) => ({
61432
+ upload: async (file2, options) => {
61433
+ const organizationId = await client.organizationId();
61434
+ const query = options.randomPrefix === false ? "?disableRandomPrefix=true" : "";
61435
+ const res = await client.fetch(
61436
+ `/v2/storage/${organizationId}/stream${query}`,
61437
+ {
61438
+ method: "POST",
61439
+ headers: {
61440
+ "Content-Type": "application/octet-stream",
61441
+ "x-file-name": options.name
61442
+ },
61443
+ body: file2
61444
+ }
61445
+ );
61446
+ if (!res.ok)
61447
+ throw apiError("storage upload", res, await res.text(), organizationId);
61448
+ const { path } = await res.json();
61449
+ const key = path.startsWith(`${organizationId}/`) ? path.slice(organizationId.length + 1) : path;
61450
+ return { key, path };
61451
+ },
61452
+ download: async (key) => {
61453
+ const organizationId = await client.organizationId();
61454
+ const res = await client.fetch(
61455
+ `/v2/storage/${organizationId}/${encodeURIComponent(key)}`,
61456
+ { redirect: "follow" }
61457
+ );
61458
+ if (!res.ok)
61459
+ throw apiError("storage download", res, await res.text(), organizationId);
61460
+ return res;
61461
+ },
61462
+ exists: async (key) => {
61463
+ const organizationId = await client.organizationId();
61464
+ const res = await client.fetch(
61465
+ `/v2/storage/${organizationId}/${encodeURIComponent(key)}/exists`
61466
+ );
61467
+ if (res.status === 404) return null;
61468
+ if (!res.ok)
61469
+ throw apiError("storage exists", res, await res.text(), organizationId);
61470
+ return await res.json();
61471
+ },
61472
+ head: async (key, options) => {
61473
+ const organizationId = await client.organizationId();
61474
+ const res = await client.fetch(
61475
+ `/v2/storage/${organizationId}/${encodeURIComponent(key)}/head?numLines=${String(options?.lines ?? 50)}`
61476
+ );
61477
+ const raw2 = await res.text();
61478
+ if (!res.ok) throw apiError("storage head", res, raw2, organizationId);
61479
+ return raw2;
61480
+ },
61481
+ presign: async (key, options) => {
61482
+ const organizationId = await client.organizationId();
61483
+ const res = await client.fetch(
61484
+ `/v2/storage/${organizationId}/${encodeURIComponent(key)}/presign?expiresIn=${String(options?.expiresIn ?? 60 * 60 * 24 * 7)}`
61485
+ );
61486
+ const raw2 = await res.text();
61487
+ if (!res.ok) throw apiError("storage presign", res, raw2, organizationId);
61488
+ return raw2.trim();
61489
+ }
61490
+ });
61491
+
61492
+ // ../sdk/src/tableSpaceRun.ts
61493
+ var clusterInfo = async (client) => {
61494
+ const res = await client.fetch("/v2/cluster-info");
61495
+ const raw2 = await res.text();
61496
+ if (!res.ok)
61497
+ throw apiError("cluster-info", res, raw2, await client.organizationId());
61498
+ return JSON.parse(raw2);
61499
+ };
61500
+ var connectionString = async (client, tableSpaceId, secret) => {
61501
+ const { postgres } = await clusterInfo(client);
61502
+ return `postgresql://overai:${encodeURIComponent(secret)}@${postgres.host}:${String(postgres.port)}/${tableSpaceId}?sslmode=${postgres.sslMode}${postgres.sslMode === "verify-full" ? "&sslrootcert=system" : ""}`;
61503
+ };
61504
+ var tableSpaceResource = (client, id) => ({
61505
+ /*
61506
+ * The controlled /run endpoint (our auth + audit). `params` switches to the
61507
+ * parameterized `unsafe` shape so identifiers stay injection-safe;
61508
+ * ignoreCamelCase keeps real column names.
61509
+ */
61510
+ sql: async (query, options) => {
61511
+ const organizationId = await client.organizationId();
61512
+ const res = await client.fetch(
61513
+ `/v2/table-spaces/${organizationId}/${id}/run`,
61514
+ {
61515
+ method: "POST",
61516
+ headers: { "Content-Type": "application/json" },
61517
+ body: JSON.stringify(
61518
+ options?.params == null ? { query, ignoreCamelCase: true } : { unsafe: { query, params: options.params } }
61519
+ )
61520
+ }
61521
+ );
61522
+ const raw2 = await res.text();
61523
+ const body = (() => {
61524
+ try {
61525
+ return JSON.parse(raw2);
61526
+ } catch {
61527
+ return null;
61528
+ }
61529
+ })();
61530
+ if (!res.ok || body?.ok === false)
61531
+ throw apiError("sql", res, raw2, organizationId);
61532
+ return {
61533
+ rows: body?.rows ?? [],
61534
+ rowCount: body?.rowCount ?? 0,
61535
+ resultSetCount: body?.resultSetCount ?? 1,
61536
+ durationMs: body?.durationMs ?? 0
61537
+ };
61538
+ },
61539
+ credentials: {
61540
+ create: async (input2) => {
61541
+ const data = await client.graphql(
61542
+ `mutation SdkCreateTableSpaceCredential($tableSpaceId: String!, $name: String!, $validUntil: Datetime) {
61543
+ createTableSpaceCredential(input: { tableSpaceId: $tableSpaceId, name: $name, validUntil: $validUntil }) { result }
61544
+ }`,
61545
+ {
61546
+ tableSpaceId: id,
61547
+ name: input2.name,
61548
+ validUntil: input2.validUntil?.toISOString() ?? null
61549
+ }
61550
+ );
61551
+ const result = data.createTableSpaceCredential?.result;
61552
+ if (!result) throw new Error("The server did not return a credential");
61553
+ return {
61554
+ id: result.id,
61555
+ secret: result.secret,
61556
+ connectionString: await connectionString(client, id, result.secret)
61557
+ };
61558
+ },
61559
+ list: async () => (await client.graphql(
61560
+ `query SdkTableSpaceCredentials($tableSpaceId: String!) {
61561
+ tableSpaceCredentials(condition: { tableSpaceId: $tableSpaceId }) {
61562
+ nodes { id name validUntil lastUsedAt createdAt }
61563
+ }
61564
+ }`,
61565
+ { tableSpaceId: id }
61566
+ )).tableSpaceCredentials?.nodes ?? []
61567
+ }
61568
+ });
61569
+ var tableSpaceCredentialResource = (client, credentialId) => {
61570
+ const tableSpaceId = async () => {
61571
+ const data = await client.graphql(
61572
+ `query SdkTableSpaceCredential($id: String!) {
61573
+ tableSpaceCredential(id: $id) { tableSpaceId }
61574
+ }`,
61575
+ { id: credentialId }
61576
+ );
61577
+ if (!data.tableSpaceCredential)
61578
+ throw new Error("Credential not found or you lack access to it");
61579
+ return data.tableSpaceCredential.tableSpaceId;
61580
+ };
61581
+ return {
61582
+ rotate: async () => {
61583
+ const resolvedTableSpaceId = await tableSpaceId();
61584
+ const data = await client.graphql(
61585
+ `mutation SdkRotateTableSpaceCredential($credentialId: String!) {
61586
+ rotateTableSpaceCredential(input: { credentialId: $credentialId }) { result }
61587
+ }`,
61588
+ { credentialId }
61589
+ );
61590
+ const secret = data.rotateTableSpaceCredential?.result;
61591
+ if (!secret) throw new Error("The server did not return a credential");
61592
+ return {
61593
+ secret,
61594
+ connectionString: await connectionString(
61595
+ client,
61596
+ resolvedTableSpaceId,
61597
+ secret
61598
+ )
61599
+ };
61600
+ },
61601
+ revoke: async () => {
61602
+ await tableSpaceId();
61603
+ await client.graphql(
61604
+ `mutation SdkRevokeTableSpaceCredential($id: String!, $validUntil: Datetime!) {
61605
+ updateTableSpaceCredential(input: { id: $id, patch: { validUntil: $validUntil } }) {
61606
+ tableSpaceCredential { id }
61607
+ }
61608
+ }`,
61609
+ { id: credentialId, validUntil: (/* @__PURE__ */ new Date()).toISOString() }
61610
+ );
61611
+ }
61612
+ };
61613
+ };
61614
+
60788
61615
  // ../sdk/src/tokens.ts
60789
61616
  var exchangeSchema = external_exports.object({ token: external_exports.string(), expiresIn: external_exports.number() });
60790
61617
  var staticToken = (jwt2) => () => jwt2;
@@ -60885,6 +61712,17 @@ var OverAIClient = class {
60885
61712
  headers.set("Authorization", `Bearer ${token}`);
60886
61713
  return fetch(`${this.baseUrl}${path}`, { ...init, headers });
60887
61714
  }
61715
+ /** The bearer the next request would send. For hosts that build their own requests, like `createEnv`. */
61716
+ token() {
61717
+ return Promise.resolve(this.#token());
61718
+ }
61719
+ /** The org the token source's JWT names; every org-scoped REST path takes it. */
61720
+ async organizationId() {
61721
+ const org = (this.#claims ?? decodeJwtClaims(await this.#token())).org;
61722
+ if (org == null || org === "")
61723
+ throw new Error("The token carries no organization claim.");
61724
+ return org;
61725
+ }
60888
61726
  /**
60889
61727
  * POST /v2/graphql. Throws with the server's error messages — callers (and
60890
61728
  * agents driving them) need the actual reason, not a status code.
@@ -60971,21 +61809,68 @@ var OverAIClient = class {
60971
61809
  create: (input2) => createDataFile(this, input2)
60972
61810
  };
60973
61811
  workspaces = workspaceCollection(this);
61812
+ runners = runnerCollection(this);
61813
+ storage = storageApi(this);
60974
61814
  /** Its `deploy` checks deployed callers first; see `FunctionResource`. */
60975
61815
  fn(id) {
60976
- return functionResource(this, id);
61816
+ return { ...functionResource(this, id), ...functionRun(this, id) };
60977
61817
  }
60978
61818
  page(id) {
60979
61819
  return codeResource(webPageSource(this), id);
60980
61820
  }
60981
61821
  printablePage(id) {
60982
- return codeResource(printablePageSource(this), id);
61822
+ return {
61823
+ ...codeResource(printablePageSource(this), id),
61824
+ ...printablePageRender(this, id)
61825
+ };
60983
61826
  }
60984
61827
  automation(id) {
60985
- return versionedResource(automationSource(this), id);
61828
+ return {
61829
+ ...versionedResource(automationSource(this), id),
61830
+ ...automationRunApi(this, id)
61831
+ };
60986
61832
  }
60987
61833
  agent(id) {
60988
- return versionedResource(agentSource(this), id);
61834
+ return {
61835
+ ...versionedResource(agentSource(this), id),
61836
+ ...agentRun(this, id)
61837
+ };
61838
+ }
61839
+ /** A run addresses no automation, so its state is read by run id alone. */
61840
+ automationRunState(runId) {
61841
+ return automationRunState(this, runId);
61842
+ }
61843
+ tableSpace(id) {
61844
+ return tableSpaceResource(this, id);
61845
+ }
61846
+ tableSpaceCredential(credentialId) {
61847
+ return tableSpaceCredentialResource(this, credentialId);
61848
+ }
61849
+ dataFile(id) {
61850
+ return dataFileResource(this, id);
61851
+ }
61852
+ dataStream(id) {
61853
+ return dataStreamResource(this, id);
61854
+ }
61855
+ knowledgeBase(id) {
61856
+ return knowledgeBaseResource(this, id);
61857
+ }
61858
+ integration(id) {
61859
+ return integrationResource(this, id);
61860
+ }
61861
+ runner(id) {
61862
+ return runnerResource(this, id);
61863
+ }
61864
+ clusterInfo() {
61865
+ return clusterInfo(this);
61866
+ }
61867
+ /** The platform's authenticated lint, which knows the workspace's resources. */
61868
+ lint(input2) {
61869
+ return lint(this, input2);
61870
+ }
61871
+ /** Files a bug report or feature request with OverAI. */
61872
+ report(kind, details, options) {
61873
+ return report(this, kind, details, options);
60989
61874
  }
60990
61875
  workspace(id) {
60991
61876
  return workspaceResource(this, id);
@@ -61279,7 +62164,6 @@ var cliClient = () => createClient({
61279
62164
  baseUrl: apiBaseUrl(),
61280
62165
  token: async () => (await getAccessToken()).token
61281
62166
  });
61282
- var v2Fetch = async (path, init) => cliClient().fetch(path, init);
61283
62167
  var v2Error = (label, res, rawBody) => withOrgHint(apiError(label, res, rawBody, scopedOrgId()));
61284
62168
  var ORG_HINT = "\n If it lives in another org, name that org: overai --org <ref> ... (`overai org list` shows them, `overai org alias` your shorthands).";
61285
62169
  var withOrgHint = (error62) => {
@@ -61287,7 +62171,6 @@ var withOrgHint = (error62) => {
61287
62171
  error62.message = `${error62.message}${ORG_HINT}`;
61288
62172
  return error62;
61289
62173
  };
61290
- var userTrpcMutation = async (procedure, input2) => cliClient().trpc(procedure, input2);
61291
62174
 
61292
62175
  // src/args.ts
61293
62176
  import { readFileSync as readFileSync3 } from "fs";
@@ -61321,6 +62204,15 @@ var flagValue = (args, ...names) => {
61321
62204
  }
61322
62205
  return void 0;
61323
62206
  };
62207
+ function integerFlag(args, name2, fallback2) {
62208
+ const names = typeof name2 === "string" ? [name2] : [...name2];
62209
+ const raw2 = flagValue(args, ...names);
62210
+ if (raw2 == null) return fallback2;
62211
+ const value = Number(raw2);
62212
+ if (!Number.isInteger(value))
62213
+ throw new Error(`${names[0] ?? ""} must be a whole number.`);
62214
+ return value;
62215
+ }
61324
62216
  var stdinClaimedBy = null;
61325
62217
  var readStdin = async (claimant = "stdin") => {
61326
62218
  if (stdinClaimedBy != null) {
@@ -61481,13 +62373,21 @@ var pinOrgFromClone = () => {
61481
62373
  } catch {
61482
62374
  }
61483
62375
  };
62376
+ var IGNORE_FILES = [".ignore", ".gitignore"];
62377
+ var IGNORE_ALL = "*\n";
61484
62378
  var writeManifest = (root, manifest) => {
61485
- mkdirSync2(join3(root, MANIFEST_DIR), { recursive: true });
62379
+ const dir = join3(root, MANIFEST_DIR);
62380
+ mkdirSync2(dir, { recursive: true });
61486
62381
  writeFileSync2(
61487
- join3(root, MANIFEST_DIR, MANIFEST_FILE),
62382
+ join3(dir, MANIFEST_FILE),
61488
62383
  `${JSON.stringify(manifest, null, 2)}
61489
62384
  `
61490
62385
  );
62386
+ for (const name2 of IGNORE_FILES) {
62387
+ const path = join3(dir, name2);
62388
+ if (!existsSync2(path) || readFileSync4(path, "utf8") !== IGNORE_ALL)
62389
+ writeFileSync2(path, IGNORE_ALL);
62390
+ }
61491
62391
  };
61492
62392
  var basePath = (root, relPath) => join3(root, MANIFEST_DIR, BASE_DIR, relPath);
61493
62393
  var writeSynced = (root, relPath, content) => {
@@ -61834,17 +62734,6 @@ var whoami = (args = []) => {
61834
62734
 
61835
62735
  // src/commands/agent.ts
61836
62736
  import { createInterface } from "readline";
61837
-
61838
- // src/gql.ts
61839
- var gqlRequest = async (query, variables) => {
61840
- try {
61841
- return await cliClient().graphql(query, variables);
61842
- } catch (error62) {
61843
- throw withOrgHint(error62);
61844
- }
61845
- };
61846
-
61847
- // src/commands/agent.ts
61848
62737
  var USAGE = [
61849
62738
  "Usage:",
61850
62739
  " overai agent generate <id> --message <text|@file|@->",
@@ -61857,210 +62746,51 @@ var generate = async (args, id) => {
61857
62746
  "--message"
61858
62747
  );
61859
62748
  if (message == null) throw new Error("Agent generate requires --message.");
61860
- const { organizationId } = await getAccessToken();
61861
- const response = await v2Fetch(
61862
- `/v2/agents/${organizationId}/${id}/generate/text`,
61863
- {
61864
- method: "POST",
61865
- headers: { "Content-Type": "application/json" },
61866
- body: JSON.stringify({ message })
61867
- }
61868
- );
61869
- const raw2 = await response.text();
61870
- if (!response.ok) throw v2Error("agent generate", response, raw2);
61871
62749
  printResult(
61872
- JSON.parse(raw2),
62750
+ await cliClient().agent(id).generate(message),
61873
62751
  resolveFormat(flagValue(args, "-o", "--output"))
61874
62752
  );
61875
62753
  };
61876
- var deployedVersionNumber = async (id) => {
61877
- const deployed = await gqlRequest(
61878
- `query CliAgentDeployedVersion($id: String!) {
61879
- agentDeployedVersions(condition: { agentId: $id }) { nodes { versionNumber } }
61880
- }`,
61881
- { id }
61882
- );
61883
- const versionNumber = deployed.agentDeployedVersions?.nodes[0]?.versionNumber;
61884
- if (versionNumber == null) {
61885
- throw new Error(
61886
- `Agent ${id} has no deployed version; pass --version <n> to inspect a saved one.`
61887
- );
61888
- }
61889
- return versionNumber;
61890
- };
61891
62754
  var tools = async (args, id) => {
61892
- const versionFlag = flagValue(args, "--version", "-v");
61893
- if (versionFlag != null && !Number.isInteger(Number(versionFlag))) {
61894
- throw new Error("--version must be an integer.");
61895
- }
61896
- const version2 = versionFlag == null ? await deployedVersionNumber(id) : Number(versionFlag);
61897
- const data = await gqlRequest(
61898
- `query CliAgentTools($id: String!, $version: Int!) {
61899
- agent(id: $id) {
61900
- name
61901
- agentVersions(first: 1, condition: { versionNumber: $version }) {
61902
- nodes {
61903
- versionNumber
61904
- model
61905
- agentVersionFunctionsByAgentIdAndVersionNumber(orderBy: [FUNCTION_ID_ASC]) {
61906
- nodes { functionId function { name } }
61907
- }
61908
- agentVersionTableSpacesByAgentIdAndVersionNumber(orderBy: [TABLE_SPACE_ID_ASC]) {
61909
- nodes { tableSpaceId accessMode }
61910
- }
61911
- agentVersionAgentsByAgentIdAndVersionNumber(orderBy: [ATTACHED_AGENT_ID_ASC]) {
61912
- nodes { attachedAgentId }
61913
- }
61914
- agentVersionKnowledgeBasesByAgentIdAndVersionNumber(orderBy: [KNOWLEDGE_BASE_ID_ASC]) {
61915
- nodes { knowledgeBaseId }
61916
- }
61917
- agentVersionBuiltinToolSetsByAgentIdAndVersionNumber(orderBy: [TOOL_SET_ASC]) {
61918
- nodes { toolSet }
61919
- }
61920
- }
61921
- }
61922
- }
61923
- }`,
61924
- { id, version: version2 }
61925
- );
61926
- if (data.agent == null) throw new Error(`Agent ${id} not found.`);
61927
- const agentVersion = data.agent.agentVersions.nodes[0];
61928
- if (agentVersion == null) {
61929
- throw new Error(`Agent ${id} has no version ${String(version2)}.`);
61930
- }
61931
- const rows = [
61932
- ...agentVersion.agentVersionFunctionsByAgentIdAndVersionNumber.nodes.map(
61933
- (link) => ({
61934
- kind: "function",
61935
- name: link.function?.name ?? link.functionId,
61936
- detail: link.functionId
61937
- })
61938
- ),
61939
- ...agentVersion.agentVersionTableSpacesByAgentIdAndVersionNumber.nodes.map(
61940
- (link) => ({
61941
- kind: "table space",
61942
- name: link.tableSpaceId,
61943
- detail: link.accessMode
61944
- })
61945
- ),
61946
- ...agentVersion.agentVersionAgentsByAgentIdAndVersionNumber.nodes.map(
61947
- (link) => ({
61948
- kind: "sub-agent",
61949
- name: link.attachedAgentId,
61950
- detail: ""
61951
- })
61952
- ),
61953
- ...agentVersion.agentVersionKnowledgeBasesByAgentIdAndVersionNumber.nodes.map(
61954
- (link) => ({
61955
- kind: "knowledge base",
61956
- name: link.knowledgeBaseId,
61957
- detail: ""
61958
- })
61959
- ),
61960
- ...agentVersion.agentVersionBuiltinToolSetsByAgentIdAndVersionNumber.nodes.map(
61961
- (link) => ({ kind: "built-in", name: link.toolSet, detail: "" })
61962
- )
61963
- ];
62755
+ const version2 = integerFlag(args, ["--version", "-v"]);
62756
+ const result = await cliClient().agent(id).tools(version2 ?? "deployed");
61964
62757
  const format2 = resolveFormat(flagValue(args, "-o", "--output"));
61965
62758
  if (format2 === "json") {
61966
- printResult(
61967
- {
61968
- agent: data.agent.name,
61969
- version: agentVersion.versionNumber,
61970
- model: agentVersion.model,
61971
- tools: rows
61972
- },
61973
- format2
61974
- );
62759
+ printResult(result, format2);
61975
62760
  return;
61976
62761
  }
61977
62762
  if (format2 === "table") {
61978
62763
  process.stdout.write(
61979
- `${data.agent.name} \xB7 version ${String(agentVersion.versionNumber)} \xB7 ${agentVersion.model}
62764
+ `${result.agent} \xB7 version ${String(result.version)} \xB7 ${result.model}
61980
62765
  `
61981
62766
  );
61982
62767
  }
61983
- printResult(rows, format2);
62768
+ printResult(result.tools, format2);
61984
62769
  };
61985
- var printStreamLine = (line) => {
61986
- if (!line.startsWith("data: ")) return;
61987
- const payload = line.slice(6);
61988
- if (payload === "[DONE]") return;
61989
- let event;
61990
- try {
61991
- event = JSON.parse(payload);
61992
- } catch {
61993
- return;
61994
- }
61995
- if (event.type === "text-delta" && event.delta != null) {
61996
- process.stdout.write(event.delta);
61997
- }
61998
- if (event.type === "error") {
61999
- process.stderr.write(`
62770
+ var send = async (agent, message, conversationId) => {
62771
+ const response = await agent.chat(
62772
+ [{ role: "user", parts: [{ type: "text", text: message }] }],
62773
+ { conversationId }
62774
+ );
62775
+ for await (const event of agentStreamEvents(response)) {
62776
+ if (event.type === "text-delta") process.stdout.write(event.delta);
62777
+ else process.stderr.write(`
62000
62778
  ${event.errorText ?? "agent turn failed"}
62001
62779
  `);
62002
62780
  }
62003
- };
62004
- var streamReply = async (response) => {
62005
- const body = response.body;
62006
- if (body == null) return;
62007
- const decoder = new TextDecoder();
62008
- let buffer = "";
62009
- for await (const chunk of body) {
62010
- buffer += decoder.decode(chunk, { stream: true });
62011
- const lines = buffer.split("\n");
62012
- buffer = lines.pop() ?? "";
62013
- lines.forEach(printStreamLine);
62014
- }
62015
- printStreamLine(buffer + decoder.decode());
62016
- };
62017
- var send = async (organizationId, id, message, conversationId) => {
62018
- const response = await v2Fetch(`/v2/agents/${organizationId}/${id}`, {
62019
- method: "POST",
62020
- headers: { "Content-Type": "application/json" },
62021
- body: JSON.stringify({
62022
- messages: [{ role: "user", parts: [{ type: "text", text: message }] }],
62023
- conversationId
62024
- })
62025
- });
62026
- if (!response.ok) {
62027
- throw v2Error("agent chat", response, await response.text());
62028
- }
62029
- await streamReply(response);
62030
62781
  process.stdout.write("\n");
62031
62782
  };
62032
- var createConversation = async (id, firstMessage) => {
62033
- const versionNumber = await deployedVersionNumber(id);
62034
- const created = await gqlRequest(
62035
- `mutation CliCreateAgentConversation($input: CreateAgentConversationInput!) {
62036
- createAgentConversation(input: $input) { agentConversation { id } }
62037
- }`,
62038
- {
62039
- input: {
62040
- agentConversation: {
62041
- agentId: id,
62042
- versionNumber,
62043
- title: firstMessage.length > 50 ? `${firstMessage.slice(0, 47)}...` : firstMessage
62044
- }
62045
- }
62046
- }
62047
- );
62048
- const conversationId = created.createAgentConversation?.agentConversation?.id;
62049
- if (conversationId == null)
62050
- throw new Error("Failed to create a conversation.");
62051
- return conversationId;
62052
- };
62053
62783
  var chat = async (args, id) => {
62054
- const { organizationId } = await getAccessToken();
62784
+ const agent = cliClient().agent(id);
62055
62785
  let conversationId = flagValue(args, "--conversation", "-c") ?? null;
62056
62786
  const single = await readValue(
62057
62787
  flagValue(args, "--message", "-m"),
62058
62788
  "--message"
62059
62789
  );
62060
62790
  if (single != null) {
62061
- conversationId ??= await createConversation(id, single);
62791
+ conversationId ??= await agent.createConversation(single);
62062
62792
  try {
62063
- await send(organizationId, id, single, conversationId);
62793
+ await send(agent, single, conversationId);
62064
62794
  } finally {
62065
62795
  process.stderr.write(`Conversation: ${conversationId}
62066
62796
  `);
@@ -62087,11 +62817,12 @@ var chat = async (args, id) => {
62087
62817
  continue;
62088
62818
  }
62089
62819
  try {
62090
- conversationId ??= await createConversation(id, message);
62091
- await send(organizationId, id, message, conversationId);
62820
+ conversationId ??= await agent.createConversation(message);
62821
+ await send(agent, message, conversationId);
62092
62822
  } catch (error62) {
62823
+ const hinted = withOrgHint(error62);
62093
62824
  process.stderr.write(
62094
- `${error62 instanceof Error ? error62.message : String(error62)}
62825
+ `${hinted instanceof Error ? hinted.message : String(hinted)}
62095
62826
  `
62096
62827
  );
62097
62828
  }
@@ -62123,53 +62854,24 @@ var runAgent = async (args) => {
62123
62854
 
62124
62855
  // src/commands/automation.ts
62125
62856
  var runRuns = async (automationId, args) => {
62126
- const limit2 = Number(flagValue(args, "--limit") ?? 20);
62127
- const data = await gqlRequest(
62128
- `query CliAutomationRuns($automationId: String!, $first: Int!) {
62129
- automationRuns(condition: { automationId: $automationId }, orderBy: [STARTED_AT_DESC], first: $first) {
62130
- nodes { id startedAt endedAt errored isCancelled manuallyTriggered version { num } }
62131
- }
62132
- }`,
62133
- // Over-fetch when status-filtering so --failed still fills the limit.
62134
- {
62135
- automationId,
62136
- first: args.includes("--failed") || args.includes("--running") ? limit2 * 5 : limit2
62137
- }
62138
- );
62139
- let runs = data.automationRuns.nodes;
62140
- if (args.includes("--failed")) runs = runs.filter((r) => r.errored);
62141
- if (args.includes("--running")) {
62142
- runs = runs.filter((r) => r.endedAt == null && !r.isCancelled);
62143
- }
62144
62857
  printResult(
62145
- runs.slice(0, limit2).map((r) => ({
62146
- id: r.id,
62147
- version: r.version?.num,
62148
- startedAt: r.startedAt,
62149
- endedAt: r.endedAt,
62150
- status: r.errored ? "errored" : r.isCancelled ? "cancelled" : r.endedAt == null ? "running" : "ok",
62151
- manual: r.manuallyTriggered
62152
- })),
62858
+ await cliClient().automation(automationId).runs({
62859
+ limit: integerFlag(args, "--limit", 20),
62860
+ failed: args.includes("--failed"),
62861
+ running: args.includes("--running")
62862
+ }),
62153
62863
  resolveFormat(flagValue(args, "-o", "--output"))
62154
62864
  );
62155
62865
  };
62156
62866
  var runState = async (runId, args) => {
62157
- const data = await gqlRequest(
62158
- `query CliAutomationRunState($id: String!) {
62159
- automationRun(id: $id) { state }
62160
- }`,
62161
- { id: runId }
62162
- );
62163
- if (data.automationRun == null) throw new Error(`Run ${runId} not found.`);
62867
+ const state = await cliClient().automationRunState(runId);
62164
62868
  const node2 = flagValue(args, "--node");
62165
- const state = data.automationRun.state ?? {};
62166
62869
  printResult(
62167
62870
  node2 != null ? { [node2]: state[node2] ?? "(no state for this node)" } : state,
62168
62871
  resolveFormat(flagValue(args, "-o", "--output"))
62169
62872
  );
62170
62873
  };
62171
62874
  var runTrigger = async (automationId, args) => {
62172
- const { organizationId } = await getAccessToken();
62173
62875
  const body = {};
62174
62876
  if (args.includes("-")) {
62175
62877
  Object.assign(
@@ -62184,18 +62886,10 @@ var runTrigger = async (automationId, args) => {
62184
62886
  if (eq > 0) body[pair.slice(0, eq)] = pair.slice(eq + 1);
62185
62887
  }
62186
62888
  }
62187
- const res = await v2Fetch(
62188
- `/v2/automations/${organizationId}/${automationId}/run`,
62189
- {
62190
- method: "POST",
62191
- headers: { "Content-Type": "application/json" },
62192
- body: JSON.stringify(body)
62193
- }
62889
+ printResult(
62890
+ await cliClient().automation(automationId).run(body),
62891
+ resolveFormat(flagValue(args, "-o", "--output"))
62194
62892
  );
62195
- const raw2 = await res.text();
62196
- if (!res.ok) throw v2Error("automation run", res, raw2);
62197
- const responseBody = JSON.parse(raw2);
62198
- printResult(responseBody, resolveFormat(flagValue(args, "-o", "--output")));
62199
62893
  };
62200
62894
  var runAutomation = async (args) => {
62201
62895
  const [sub, id, ...rest] = args;
@@ -62300,29 +62994,7 @@ var adapterFor = (type) => {
62300
62994
  };
62301
62995
 
62302
62996
  // src/commands/runner.ts
62303
- var createActionRunner = async (workspaceId, name2) => {
62304
- const identityResult = await gqlRequest(
62305
- `mutation CliCreateMachineIdentity($name: String!) {
62306
- createMachineIdentity(input: { name: $name }) {
62307
- identity { id }
62308
- }
62309
- }`,
62310
- { name: name2 }
62311
- );
62312
- const identityId = identityResult.createMachineIdentity?.identity?.id;
62313
- if (identityId == null) throw new Error("Failed to create machine identity");
62314
- const runnerResult = await gqlRequest(
62315
- `mutation CliCreateActionRunner($identityId: String!, $workspaceId: String!) {
62316
- createActionRunner(
62317
- input: { actionRunner: { identityId: $identityId, workspaceId: $workspaceId } }
62318
- ) { actionRunner { id } }
62319
- }`,
62320
- { identityId, workspaceId }
62321
- );
62322
- const actionRunnerId = runnerResult.createActionRunner?.actionRunner?.id;
62323
- if (actionRunnerId == null) throw new Error("Failed to create action runner");
62324
- return { actionRunnerId, identityId };
62325
- };
62997
+ var createActionRunner = (workspaceId, name2) => cliClient().runners.create({ workspaceId, name: name2 });
62326
62998
  var runCreate = async (workspaceId, args) => {
62327
62999
  const name2 = flagValue(args, "--name") ?? "Action Runner";
62328
63000
  printResult(
@@ -62331,53 +63003,12 @@ var runCreate = async (workspaceId, args) => {
62331
63003
  );
62332
63004
  };
62333
63005
  var runToken = async (runnerId, args) => {
62334
- const note = flagValue(args, "--note") ?? "cli";
62335
- const result = await gqlRequest(
62336
- `mutation CliCreateActionRunnerToken($actionRunnerId: String!, $note: String) {
62337
- createActionRunnerToken(
62338
- input: { actionRunnerId: $actionRunnerId, note: $note }
62339
- ) { result }
62340
- }`,
62341
- { actionRunnerId: runnerId, note }
62342
- );
62343
- const secret = result.createActionRunnerToken?.result;
62344
- if (secret == null) throw new Error("Failed to mint runner token");
63006
+ const secret = await cliClient().runner(runnerId).token({ note: flagValue(args, "--note") ?? "cli" });
62345
63007
  printResult(
62346
63008
  { runnerId, secret },
62347
63009
  resolveFormat(flagValue(args, "-o", "--output"))
62348
63010
  );
62349
63011
  };
62350
- var resolveWorkspaceId = async (runnerId) => {
62351
- const data = await gqlRequest(
62352
- `query CliResolveRunner($id: String!) {
62353
- actionRunners(condition: { id: $id }, first: 1) { nodes { workspaceId } }
62354
- }`,
62355
- { id: runnerId }
62356
- );
62357
- const workspaceId = data.actionRunners.nodes[0]?.workspaceId;
62358
- if (workspaceId == null) {
62359
- throw new Error(
62360
- `Unknown runner ${runnerId} (or it has no workspace). Try: overai runner list`
62361
- );
62362
- }
62363
- return workspaceId;
62364
- };
62365
- var enqueueAction = async (runnerId, actionId, inputs, args) => {
62366
- const { organizationId } = await getAccessToken();
62367
- const workspaceId = await resolveWorkspaceId(runnerId);
62368
- const res = await v2Fetch(
62369
- `/v2/actions/${organizationId}/${workspaceId}/run`,
62370
- {
62371
- method: "POST",
62372
- headers: { "Content-Type": "application/json" },
62373
- body: JSON.stringify({ actionId, inputs, actionRunnerId: runnerId })
62374
- }
62375
- );
62376
- const raw2 = await res.text();
62377
- if (!res.ok) throw v2Error("runner run", res, raw2);
62378
- const body = JSON.parse(raw2);
62379
- printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
62380
- };
62381
63012
  var runRun = async (runnerId, args) => {
62382
63013
  const code = await readValue(flagValue(args, "--code"), "--code") ?? (process.stdin.isTTY ? "" : (await readStdin("the code")).trim());
62383
63014
  if (code.length === 0) {
@@ -62389,48 +63020,30 @@ var runRun = async (runnerId, args) => {
62389
63020
  if (interpreter !== "shell" && interpreter !== "powershell") {
62390
63021
  throw new Error("--interpreter must be 'shell' or 'powershell'.");
62391
63022
  }
62392
- await enqueueAction(
62393
- runnerId,
62394
- "RUNNER_RUN_COMMAND",
62395
- { actionRunnerId: runnerId, interpreter, code },
62396
- args
62397
- );
62398
- };
62399
- var runUpload = async (runnerId, args) => {
62400
- const path = flagValue(args, "--path");
62401
- if (path == null || path.length === 0) {
62402
- throw new Error("Provide a file with --path '<path>'.");
62403
- }
62404
- await enqueueAction(
62405
- runnerId,
62406
- "RUNNER_UPLOAD_FILE",
62407
- { actionRunnerId: runnerId, filePath: path },
62408
- args
63023
+ printResult(
63024
+ await cliClient().runner(runnerId).run({ code, interpreter }),
63025
+ resolveFormat(flagValue(args, "-o", "--output"))
62409
63026
  );
62410
63027
  };
62411
- var runList = async (args) => {
62412
- const workspaceId = args.find((a) => !a.startsWith("-"));
62413
- const limit2 = Number(flagValue(args, "--limit") ?? 50);
62414
- const data = await gqlRequest(
62415
- `query CliRunnerList($workspaceId: String, $first: Int!) {
62416
- actionRunners(condition: { workspaceId: $workspaceId }, first: $first) {
62417
- nodes { id identity { name } workspaceId isCloud isArchived createdAt }
62418
- }
62419
- }`,
62420
- { workspaceId: workspaceId ?? null, first: limit2 }
62421
- );
62422
- printResult(
62423
- data.actionRunners.nodes.map((r) => ({
62424
- id: r.id,
62425
- name: r.identity?.name ?? null,
62426
- workspaceId: r.workspaceId,
62427
- kind: r.isCloud ? "cloud" : "self-hosted",
62428
- archived: r.isArchived,
62429
- createdAt: r.createdAt
62430
- })),
63028
+ var runUpload = async (runnerId, args) => {
63029
+ const path = flagValue(args, "--path");
63030
+ if (path == null || path.length === 0) {
63031
+ throw new Error("Provide a file with --path '<path>'.");
63032
+ }
63033
+ printResult(
63034
+ await cliClient().runner(runnerId).upload(path),
62431
63035
  resolveFormat(flagValue(args, "-o", "--output"))
62432
63036
  );
62433
63037
  };
63038
+ var runList = async (args) => {
63039
+ const workspaceId = args.find((a) => !a.startsWith("-"));
63040
+ const limit2 = integerFlag(args, "--limit", 50);
63041
+ const runners = await cliClient().runners.list({
63042
+ ...workspaceId != null ? { workspaceId } : {},
63043
+ limit: limit2
63044
+ });
63045
+ printResult(runners, resolveFormat(flagValue(args, "-o", "--output")));
63046
+ };
62434
63047
  var USAGE2 = "Usage: overai runner create <workspaceId> [--name <name>]\n overai runner token <runnerId> [--note <note>]\n overai runner run <runnerId> [--interpreter shell|powershell] --code '<code>'\n overai runner upload <runnerId> --path '<path>'\n overai runner list [workspaceId] [--limit <n>]\n";
62435
63048
  var runRunner = async (args) => {
62436
63049
  const [sub, id, ...rest] = args;
@@ -62574,29 +63187,26 @@ var runCreate2 = async (args) => {
62574
63187
  };
62575
63188
 
62576
63189
  // src/commands/dataFile.ts
62577
- import { readFileSync as readFileSync5, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
63190
+ import { openAsBlob, writeFileSync as writeFileSync3 } from "fs";
62578
63191
  var runDataFile = async (args) => {
62579
63192
  const [operation, id, path] = args;
62580
63193
  if (id == null || !["upload", "download", "head"].includes(operation ?? "")) {
62581
- throw new Error("Usage: overai data-file upload <id> <path> [-m <message>] | data-file download <id> [path|-] | data-file head <id> [--lines N]");
63194
+ throw new Error(
63195
+ "Usage: overai data-file upload <id> <path> [-m <message>] | data-file download <id> [path|-] | data-file head <id> [--lines N]"
63196
+ );
62582
63197
  }
62583
- const { organizationId } = await getAccessToken();
63198
+ const dataFile = cliClient().dataFile(id);
62584
63199
  if (operation === "upload") {
62585
- if (path == null || path === "-") throw new Error("Upload requires a file path.");
62586
- const url2 = await userTrpcMutation("dataFiles.getDataFileUploadUrl", {
62587
- dataFileId: id,
62588
- fileSize: statSync2(path).size,
63200
+ if (path == null || path === "-")
63201
+ throw new Error("Upload requires a file path.");
63202
+ await dataFile.upload(await openAsBlob(path), {
62589
63203
  message: flagValue(args, "-m", "--message") ?? ""
62590
63204
  });
62591
- const response2 = await fetch(url2, { method: "PUT", body: readFileSync5(path) });
62592
- if (!response2.ok) throw v2Error("data-file upload", response2, await response2.text());
62593
63205
  process.stdout.write(`${id}
62594
63206
  `);
62595
63207
  return;
62596
63208
  }
62597
- const suffix = operation === "head" ? `/head?numLines=${encodeURIComponent(flagValue(args, "--lines") ?? "50")}` : "";
62598
- const response = await v2Fetch(`/v2/data-files/${organizationId}/${id}${suffix}`, { redirect: "follow" });
62599
- if (!response.ok) throw v2Error(`data-file ${String(operation)}`, response, await response.text());
63209
+ const response = operation === "head" ? await dataFile.head({ lines: integerFlag(args, "--lines", 50) }) : await dataFile.read();
62600
63210
  const bytes = Buffer.from(await response.arrayBuffer());
62601
63211
  if (path != null && path !== "-") writeFileSync3(path, bytes);
62602
63212
  else process.stdout.write(bytes);
@@ -62606,40 +63216,39 @@ var runDataFile = async (args) => {
62606
63216
  var runDataStream = async (args) => {
62607
63217
  const [operation, id] = args;
62608
63218
  if (id == null || operation !== "append" && operation !== "query") {
62609
- throw new Error("Usage: overai data-stream append <id> <json|@file|@-> | data-stream query <id> --start <date> --end <date> [--query <filter>] [--limit N]");
63219
+ throw new Error(
63220
+ "Usage: overai data-stream append <id> <json|@file|@-> | data-stream query <id> --start <date> --end <date> [--query <filter>] [--limit N]"
63221
+ );
62610
63222
  }
62611
- const { organizationId } = await getAccessToken();
63223
+ const dataStream = cliClient().dataStream(id);
62612
63224
  if (operation === "append") {
62613
- const raw3 = await readValue(args[2], "data stream input");
62614
- if (raw3 == null) throw new Error("Data stream append requires JSON, @<file>, or @-.");
63225
+ const raw2 = await readValue(args[2], "data stream input");
63226
+ if (raw2 == null)
63227
+ throw new Error("Data stream append requires JSON, @<file>, or @-.");
62615
63228
  const parsed = (() => {
62616
63229
  try {
62617
- return JSON.parse(raw3);
63230
+ return JSON.parse(raw2);
62618
63231
  } catch {
62619
63232
  throw new Error("Data stream input must be valid JSON.");
62620
63233
  }
62621
63234
  })();
62622
- const response2 = await v2Fetch(`/v1/data-streams/${organizationId}/${id}`, {
62623
- method: "POST",
62624
- headers: { "Content-Type": "application/json" },
62625
- body: JSON.stringify({ data: Array.isArray(parsed) ? parsed : [parsed] })
62626
- });
62627
- const appended = await response2.text();
62628
- if (!response2.ok) throw v2Error("data-stream append", response2, appended);
62629
- const body2 = JSON.parse(appended);
62630
- printResult(body2, resolveFormat(flagValue(args, "-o", "--output")));
63235
+ printResult(
63236
+ await dataStream.append(parsed),
63237
+ resolveFormat(flagValue(args, "-o", "--output"))
63238
+ );
62631
63239
  return;
62632
63240
  }
62633
63241
  const start = flagValue(args, "--start");
62634
63242
  const end = flagValue(args, "--end");
62635
- if (start == null || end == null) throw new Error("Data stream query requires --start and --end.");
62636
- const query = new URLSearchParams({ startAt: start, endAt: end, query: flagValue(args, "--query") ?? "" });
62637
- const limit2 = flagValue(args, "--limit");
62638
- if (limit2 != null) query.set("limit", limit2);
62639
- const response = await v2Fetch(`/v1/data-streams/${organizationId}/${id}?${query.toString()}`);
62640
- const raw2 = await response.text();
62641
- if (!response.ok) throw v2Error("data-stream query", response, raw2);
62642
- const body = JSON.parse(raw2);
63243
+ if (start == null || end == null)
63244
+ throw new Error("Data stream query requires --start and --end.");
63245
+ const limit2 = integerFlag(args, "--limit");
63246
+ const body = await dataStream.query({
63247
+ startAt: start,
63248
+ endAt: end,
63249
+ query: flagValue(args, "--query") ?? "",
63250
+ ...limit2 != null ? { limit: limit2 } : {}
63251
+ });
62643
63252
  printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
62644
63253
  };
62645
63254
 
@@ -62654,12 +63263,21 @@ import {
62654
63263
  existsSync as existsSync3,
62655
63264
  lstatSync,
62656
63265
  mkdirSync as mkdirSync3,
62657
- readFileSync as readFileSync6,
63266
+ readFileSync as readFileSync5,
62658
63267
  unlinkSync,
62659
63268
  writeFileSync as writeFileSync4
62660
63269
  } from "fs";
62661
63270
  import { join as join4, resolve as resolve3 } from "path";
62662
63271
 
63272
+ // src/gql.ts
63273
+ var gqlRequest = async (query, variables) => {
63274
+ try {
63275
+ return await cliClient().graphql(query, variables);
63276
+ } catch (error62) {
63277
+ throw withOrgHint(error62);
63278
+ }
63279
+ };
63280
+
62663
63281
  // src/workspace/tree.ts
62664
63282
  var fetchWorkspaceChildren = async (id) => {
62665
63283
  const children = [];
@@ -62724,7 +63342,7 @@ var runClone = async (args, isShallow = false) => {
62724
63342
  `${root} is already a cloned workspace. Use \`overai pull\` to refresh.`
62725
63343
  );
62726
63344
  }
62727
- const partialCloneWorkspaceId = existsSync3(cloneMarker) ? readFileSync6(cloneMarker, "utf8").trim() : null;
63345
+ const partialCloneWorkspaceId = existsSync3(cloneMarker) ? readFileSync5(cloneMarker, "utf8").trim() : null;
62728
63346
  if (partialCloneWorkspaceId != null && partialCloneWorkspaceId !== workspaceId) {
62729
63347
  throw new Error(
62730
63348
  `${root} contains an incomplete clone of ${partialCloneWorkspaceId}. Retry that workspace or choose another directory.`
@@ -62742,7 +63360,7 @@ var runClone = async (args, isShallow = false) => {
62742
63360
  if (entry.root !== root && existsSync3(entry.root)) {
62743
63361
  if (lstatSync(entry.root).isSymbolicLink())
62744
63362
  throw new Error(`Refusing to clone into symbolic link ${entry.root}.`);
62745
- const savedId = completed ? readManifest(entry.root).workspaceId : existsSync3(marker) ? readFileSync6(marker, "utf8").trim() : null;
63363
+ const savedId = completed ? readManifest(entry.root).workspaceId : existsSync3(marker) ? readFileSync5(marker, "utf8").trim() : null;
62746
63364
  if (partialCloneWorkspaceId !== workspaceId || savedId !== entry.workspace.id)
62747
63365
  throw new Error(
62748
63366
  `${entry.root} already exists. Choose another clone directory.`
@@ -63261,7 +63879,7 @@ var runDev = async (args) => {
63261
63879
  clearTimeout(timer);
63262
63880
  });
63263
63881
  });
63264
- const port = Number(flagValue(args, "--port") ?? 4820);
63882
+ const port = integerFlag(args, "--port", 4820);
63265
63883
  await new Promise((resolve4, reject2) => {
63266
63884
  server.once("error", reject2);
63267
63885
  server.listen(port, "127.0.0.1", resolve4);
@@ -63309,54 +63927,16 @@ var runLogs = async (args) => {
63309
63927
  );
63310
63928
  }
63311
63929
  const executionId = flagValue(args, "--execution");
63930
+ const fn2 = cliClient().fn(functionId);
63931
+ const fmt = resolveFormat(flagValue(args, "-o", "--output"));
63312
63932
  if (executionId == null) {
63313
- const limit2 = Number(flagValue(args, "--limit") ?? 20);
63314
- const data2 = await gqlRequest(
63315
- `query CliFunctionExecutions($functionId: String!, $first: Int!) {
63316
- functionExecutions(condition: { functionId: $functionId }, orderBy: [CREATED_AT_DESC], first: $first) {
63317
- nodes { id createdAt httpStatusCode executionTime requestMethod requestPath versionNumber }
63318
- }
63319
- }`,
63320
- { functionId, first: limit2 }
63321
- );
63322
63933
  printResult(
63323
- data2.functionExecutions.nodes,
63324
- resolveFormat(flagValue(args, "-o", "--output"))
63934
+ await fn2.executions({ limit: integerFlag(args, "--limit", 20) }),
63935
+ fmt
63325
63936
  );
63326
63937
  return;
63327
63938
  }
63328
- const data = await gqlRequest(
63329
- `query CliFunctionExecution($functionId: String!, $id: String!) {
63330
- functionExecutions(condition: { functionId: $functionId, id: $id }, first: 1) {
63331
- nodes { id summary createdAt httpStatusCode executionTime versionNumber logsUrl inputBodyUrl outputBodyUrl }
63332
- }
63333
- }`,
63334
- { functionId, id: executionId }
63335
- );
63336
- const execution = data.functionExecutions.nodes[0];
63337
- if (execution == null) throw new Error(`Execution ${executionId} not found.`);
63338
- const fetchText = async (url2) => url2 == null ? null : await fetch(url2).then((r) => r.ok ? r.text() : null);
63339
- const [logs, input2, output2] = await Promise.all([
63340
- fetchText(execution.logsUrl),
63341
- fetchText(execution.inputBodyUrl),
63342
- fetchText(execution.outputBodyUrl)
63343
- ]);
63344
- const fmt = resolveFormat(flagValue(args, "-o", "--output"));
63345
- printResult(
63346
- {
63347
- id: execution.id,
63348
- createdAt: execution.createdAt,
63349
- httpStatusCode: execution.httpStatusCode,
63350
- executionTime: execution.executionTime,
63351
- versionNumber: execution.versionNumber,
63352
- summary: execution.summary,
63353
- input: input2,
63354
- output: output2,
63355
- logs
63356
- },
63357
- // A single nested record reads better as JSON than a 1-row table.
63358
- fmt === "table" ? "json" : fmt
63359
- );
63939
+ printResult(await fn2.execution(executionId), fmt === "table" ? "json" : fmt);
63360
63940
  };
63361
63941
  var runFn = async (args) => {
63362
63942
  if (args[0] === "logs") return runLogs(args.slice(1));
@@ -63367,8 +63947,6 @@ var runFn = async (args) => {
63367
63947
  );
63368
63948
  }
63369
63949
  const method = (flagValue(args, "--method") ?? "POST").toUpperCase();
63370
- const rawPath = flagValue(args, "--path") ?? "";
63371
- const subPath = rawPath && !rawPath.startsWith("/") ? `/${rawPath}` : rawPath;
63372
63950
  const sendsBody = !["GET", "HEAD", "DELETE"].includes(method);
63373
63951
  if (!sendsBody && (args.includes("--input") || args.includes("-"))) {
63374
63952
  process.stderr.write(
@@ -63376,16 +63954,12 @@ var runFn = async (args) => {
63376
63954
  `
63377
63955
  );
63378
63956
  }
63379
- const input2 = sendsBody ? await collectInput(args) : void 0;
63380
- const { organizationId } = await getAccessToken();
63381
- const res = await v2Fetch(
63382
- `/v2/functions/${organizationId}/${functionId}${subPath}`,
63383
- {
63384
- method,
63385
- headers: sendsBody ? { "Content-Type": "application/json" } : {},
63386
- body: sendsBody ? JSON.stringify(input2) : void 0
63387
- }
63388
- );
63957
+ const res = await cliClient().fn(functionId).call({
63958
+ method,
63959
+ path: flagValue(args, "--path"),
63960
+ headers: sendsBody ? { "Content-Type": "application/json" } : {},
63961
+ body: sendsBody ? JSON.stringify(await collectInput(args)) : void 0
63962
+ });
63389
63963
  const executionId = res.headers.get("x-execution-id");
63390
63964
  if (executionId != null) {
63391
63965
  process.stderr.write(`execution: ${executionId}
@@ -63398,11 +63972,11 @@ var runFn = async (args) => {
63398
63972
  };
63399
63973
 
63400
63974
  // src/commands/graphql.ts
63401
- import { readFileSync as readFileSync7 } from "fs";
63975
+ import { readFileSync as readFileSync6 } from "fs";
63402
63976
  var runGraphql = async (args) => {
63403
63977
  const inline = await readValue(flagValue(args, "-q", "--query"), "-q");
63404
63978
  const file2 = flagValue(args, "-f", "--file");
63405
- const query = inline ?? (file2 != null ? readFileSync7(file2, "utf8") : process.stdin.isTTY ? "" : (await readStdin("the query")).trim());
63979
+ const query = inline ?? (file2 != null ? readFileSync6(file2, "utf8") : process.stdin.isTTY ? "" : (await readStdin("the query")).trim());
63406
63980
  if (query.length === 0) {
63407
63981
  throw new Error(
63408
63982
  "Provide a query with -q '<query>', -q @<file>, -f <file>, or stdin."
@@ -63426,14 +64000,19 @@ var runGraphql = async (args) => {
63426
64000
  );
63427
64001
  }
63428
64002
  })();
63429
- const res = await v2Fetch(
63430
- args.includes("--v1") ? "/v1/graphql" : "/v2/graphql",
63431
- {
63432
- method: "POST",
63433
- headers: { "Content-Type": "application/json" },
63434
- body: JSON.stringify({ query, variables })
63435
- }
63436
- );
64003
+ const format2 = resolveFormat(flagValue(args, "-o", "--output"));
64004
+ if (!args.includes("--v1")) {
64005
+ printResult(
64006
+ await gqlRequest(query, variables),
64007
+ format2
64008
+ );
64009
+ return;
64010
+ }
64011
+ const res = await cliClient().fetch("/v1/graphql", {
64012
+ method: "POST",
64013
+ headers: { "Content-Type": "application/json" },
64014
+ body: JSON.stringify({ query, variables })
64015
+ });
63437
64016
  const raw2 = await res.text();
63438
64017
  const body = (() => {
63439
64018
  try {
@@ -63449,39 +64028,11 @@ var runGraphql = async (args) => {
63449
64028
  if (body == null) {
63450
64029
  throw new Error("graphql returned an unreadable body.");
63451
64030
  }
63452
- printResult(body.data, resolveFormat(flagValue(args, "-o", "--output")));
64031
+ printResult(body.data, format2);
63453
64032
  };
63454
64033
 
63455
64034
  // src/commands/integration.ts
63456
64035
  var USAGE3 = "Usage: overai integration configure <id> --config <json|@file|@-> | integration config <id> | integration secret set <id> <KEY> --value <@file|@-> | integration secret unset <id> <KEY>";
63457
- var setSecret = async (integrationId, key, rawValue) => {
63458
- if (rawValue == null) {
63459
- throw new Error("Integration secret set requires --value.");
63460
- }
63461
- if (!rawValue.startsWith("@")) {
63462
- throw new Error(
63463
- "--value must be @- (stdin) or @<file>, so the secret stays out of your shell history."
63464
- );
63465
- }
63466
- const value = await readValue(rawValue, "--value");
63467
- if (value == null || value === "") {
63468
- throw new Error("--value resolved to an empty string.");
63469
- }
63470
- await gqlRequest(
63471
- `mutation SetIntegrationSecret($input: UpdateIntegrationConfigInput!) {
63472
- updateIntegrationConfig(input: $input) { success }
63473
- }`,
63474
- { input: { integrationId, partialConfig: { [key]: value } } }
63475
- );
63476
- };
63477
- var unsetSecret = async (integrationId, key) => {
63478
- await gqlRequest(
63479
- `mutation UnsetIntegrationSecret($input: DeleteIntegrationConfigKeyInput!) {
63480
- deleteIntegrationConfigKey(input: $input) { success }
63481
- }`,
63482
- { input: { integrationId, key } }
63483
- );
63484
- };
63485
64036
  var runIntegration = async (args) => {
63486
64037
  const [operation] = args;
63487
64038
  if (operation === "secret") {
@@ -63489,22 +64040,36 @@ var runIntegration = async (args) => {
63489
64040
  if (action !== "set" && action !== "unset" || id2 == null || key == null || key.startsWith("-")) {
63490
64041
  throw new Error(USAGE3);
63491
64042
  }
64043
+ const integration2 = cliClient().integration(id2);
63492
64044
  if (action === "set") {
63493
- await setSecret(id2, key, flagValue(args, "--value"));
64045
+ const rawValue = flagValue(args, "--value");
64046
+ if (rawValue == null) {
64047
+ throw new Error("Integration secret set requires --value.");
64048
+ }
64049
+ if (!rawValue.startsWith("@")) {
64050
+ throw new Error(
64051
+ "--value must be @- (stdin) or @<file>, so the secret stays out of your shell history."
64052
+ );
64053
+ }
64054
+ const value = await readValue(rawValue, "--value");
64055
+ if (value == null || value === "") {
64056
+ throw new Error("--value resolved to an empty string.");
64057
+ }
64058
+ await integration2.setSecret(key, value);
63494
64059
  } else {
63495
- await unsetSecret(id2, key);
64060
+ await integration2.unsetSecret(key);
63496
64061
  }
63497
- const result2 = await userTrpcMutation(
63498
- "getMaskedIntegrationConfig",
63499
- { id: id2 }
64062
+ printResult(
64063
+ await integration2.config(),
64064
+ resolveFormat(flagValue(args, "-o", "--output"))
63500
64065
  );
63501
- printResult(result2, resolveFormat(flagValue(args, "-o", "--output")));
63502
64066
  return;
63503
64067
  }
63504
64068
  const [, id] = args;
63505
64069
  if (id == null || operation !== "configure" && operation !== "config") {
63506
64070
  throw new Error(USAGE3);
63507
64071
  }
64072
+ const integration = cliClient().integration(id);
63508
64073
  if (operation === "configure") {
63509
64074
  const raw2 = await readValue(flagValue(args, "--config"), "--config");
63510
64075
  if (raw2 == null)
@@ -63516,16 +64081,16 @@ var runIntegration = async (args) => {
63516
64081
  throw new Error("--config must be valid JSON.");
63517
64082
  }
63518
64083
  })();
63519
- await userTrpcMutation("putIntegrationConfig", { id, value });
64084
+ await integration.configure(value);
63520
64085
  }
63521
- const result = await userTrpcMutation("getMaskedIntegrationConfig", {
63522
- id
63523
- });
63524
- printResult(result, resolveFormat(flagValue(args, "-o", "--output")));
64086
+ printResult(
64087
+ await integration.config(),
64088
+ resolveFormat(flagValue(args, "-o", "--output"))
64089
+ );
63525
64090
  };
63526
64091
 
63527
64092
  // src/commands/knowledgeBase.ts
63528
- import { readFile as readFile2 } from "fs/promises";
64093
+ import { openAsBlob as openAsBlob2 } from "fs";
63529
64094
  import { basename } from "path";
63530
64095
  var runKnowledgeBase = async (args) => {
63531
64096
  const [operation, id] = args;
@@ -63534,7 +64099,7 @@ var runKnowledgeBase = async (args) => {
63534
64099
  "Usage: overai knowledge-base upload <id> <file...> | knowledge-base search <id> <query> [--limit N]"
63535
64100
  );
63536
64101
  }
63537
- const { organizationId } = await getAccessToken();
64102
+ const knowledgeBase = cliClient().knowledgeBase(id);
63538
64103
  if (operation === "upload") {
63539
64104
  const paths = args.slice(2).filter(
63540
64105
  (arg, i, all) => !arg.startsWith("-") && all[i - 1] !== "-o" && all[i - 1] !== "--output"
@@ -63543,20 +64108,11 @@ var runKnowledgeBase = async (args) => {
63543
64108
  throw new Error("Knowledge base upload requires at least one file path.");
63544
64109
  const uploads = [];
63545
64110
  for (const path of paths) {
63546
- const bytes = await readFile2(path);
63547
- const formData = new FormData();
63548
- formData.append(
63549
- "file",
63550
- new Blob([new Uint8Array(bytes)]),
64111
+ const result = await knowledgeBase.upload(
64112
+ await openAsBlob2(path),
63551
64113
  basename(path)
63552
64114
  );
63553
- const response2 = await v2Fetch(
63554
- `/v2/knowledge-bases/${organizationId}/${id}/upload`,
63555
- { method: "POST", body: formData }
63556
- );
63557
- const raw3 = await response2.text();
63558
- if (!response2.ok) throw v2Error("knowledge-base upload", response2, raw3);
63559
- uploads.push({ file: basename(path), ...JSON.parse(raw3) });
64115
+ uploads.push({ file: basename(path), ...result });
63560
64116
  }
63561
64117
  printResult(uploads, resolveFormat(flagValue(args, "-o", "--output")));
63562
64118
  return;
@@ -63564,22 +64120,9 @@ var runKnowledgeBase = async (args) => {
63564
64120
  const query = args[2];
63565
64121
  if (query == null || query.startsWith("-"))
63566
64122
  throw new Error("Knowledge base search requires a query string.");
63567
- const limit2 = flagValue(args, "--limit");
63568
- const response = await v2Fetch(
63569
- `/v2/knowledge-bases/${organizationId}/${id}/search`,
63570
- {
63571
- method: "POST",
63572
- headers: { "Content-Type": "application/json" },
63573
- body: JSON.stringify({
63574
- query,
63575
- ...limit2 != null ? { limit: Number(limit2) } : {}
63576
- })
63577
- }
63578
- );
63579
- const raw2 = await response.text();
63580
- if (!response.ok) throw v2Error("knowledge-base search", response, raw2);
64123
+ const limit2 = integerFlag(args, "--limit");
63581
64124
  printResult(
63582
- JSON.parse(raw2),
64125
+ await knowledgeBase.search(query, limit2 != null ? { limit: limit2 } : {}),
63583
64126
  resolveFormat(flagValue(args, "-o", "--output"))
63584
64127
  );
63585
64128
  };
@@ -63872,25 +64415,16 @@ var unaliasOrg = async (alias) => {
63872
64415
  // src/commands/printablePage.ts
63873
64416
  var runPrintablePage = async (args) => {
63874
64417
  const [operation, id] = args;
63875
- if (operation !== "render" || id == null) throw new Error("Usage: overai printable-page render <id> [--search-params <json|@file>] [--timeout <ms>]");
63876
- const data = await gqlRequest(
63877
- `query CliPrintablePageWorkspace($id: String!) { printablePage(id: $id) { workspaceId } }`,
63878
- { id }
63879
- );
63880
- if (data.printablePage == null) throw new Error(`Printable page ${id} not found.`);
64418
+ if (operation !== "render" || id == null)
64419
+ throw new Error(
64420
+ "Usage: overai printable-page render <id> [--search-params <json|@file>] [--timeout <ms>]"
64421
+ );
63881
64422
  const searchParams = await readValue(flagValue(args, "--search-params"), "--search-params") ?? "{}";
63882
64423
  JSON.parse(searchParams);
63883
- const { organizationId } = await getAccessToken();
63884
- const timeoutMs = Number(flagValue(args, "--timeout") ?? 6e4);
63885
- const response = await v2Fetch(`/v2/actions/${organizationId}/${data.printablePage.workspaceId}/run`, {
63886
- method: "POST",
63887
- headers: { "Content-Type": "application/json" },
63888
- body: JSON.stringify({ actionId: "PRINTABLE_PAGE_TO_PDF", inputs: { printablePageId: id, searchParams, includeDiagnostics: true } }),
63889
- signal: AbortSignal.timeout(timeoutMs)
64424
+ const body = await cliClient().printablePage(id).render({
64425
+ searchParams,
64426
+ timeoutMs: integerFlag(args, "--timeout", 6e4)
63890
64427
  });
63891
- const raw2 = await response.text();
63892
- if (!response.ok) throw v2Error("printable-page render", response, raw2);
63893
- const body = JSON.parse(raw2);
63894
64428
  printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
63895
64429
  };
63896
64430
 
@@ -64313,7 +64847,7 @@ var runRemote = async (args) => {
64313
64847
 
64314
64848
  // src/commands/update.ts
64315
64849
  import { spawnSync as spawnSync2 } from "child_process";
64316
- import { existsSync as existsSync5, readFileSync as readFileSync8 } from "fs";
64850
+ import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
64317
64851
  import { fileURLToPath } from "url";
64318
64852
  var PACKAGE_NAME = "@overaihq/cli";
64319
64853
  var packageRootUrl = () => {
@@ -64331,7 +64865,7 @@ var compareVersions = (a, b) => {
64331
64865
  };
64332
64866
  var cliVersion = () => {
64333
64867
  const pkg = JSON.parse(
64334
- readFileSync8(new URL("package.json", packageRootUrl()), "utf8")
64868
+ readFileSync7(new URL("package.json", packageRootUrl()), "utf8")
64335
64869
  );
64336
64870
  return pkg.version;
64337
64871
  };
@@ -64438,8 +64972,7 @@ var runReport = async (args) => {
64438
64972
  }
64439
64973
  })();
64440
64974
  const resources = manifest ? Object.entries(manifest.resources) : [];
64441
- await userTrpcMutation("architectIssues.report", {
64442
- ...input2,
64975
+ await cliClient().report(input2.feedbackType, input2.detailedBugReport, {
64443
64976
  source: "cli",
64444
64977
  cliContext: {
64445
64978
  cliVersion: cliVersion(),
@@ -64478,12 +65011,12 @@ var runResource = async (args) => {
64478
65011
  );
64479
65012
  return;
64480
65013
  }
64481
- const timeout = flagValue(args, "--timeout");
65014
+ const timeout = integerFlag(args, "--timeout");
64482
65015
  const patch = {
64483
65016
  name: flagValue(args, "--name"),
64484
65017
  description: flagValue(args, "--description"),
64485
65018
  integrationTypeId: flagValue(args, "--type"),
64486
- actionTimeoutSeconds: timeout == null ? void 0 : Number(timeout)
65019
+ actionTimeoutSeconds: timeout
64487
65020
  };
64488
65021
  [
64489
65022
  ["isArchived", "--archive", true],
@@ -64538,7 +65071,9 @@ var workflow = `# CLI editing workflow
64538
65071
  step users can feel; push freely, deploy deliberately.
64539
65072
 
64540
65073
  Never edit .overai/ by hand \u2014 it holds the manifest and pristine base copies
64541
- that make status/diff/fast-forward work.
65074
+ that make status/diff/fast-forward work. \`.overai/base/\` mirrors every
65075
+ resource file as last synced; edit the working copy, never the base. Git and
65076
+ ripgrep skip the folder through its own ignore files.
64542
65077
 
64543
65078
  A push conflict does not deploy anything. Normal push sends the manifest base
64544
65079
  to an atomic server check; only explicit --force omits it. Never use --force as
@@ -64617,6 +65152,19 @@ around a clone-sync mismatch; use the correct workspace folder.
64617
65152
  - Give each service its own credential. List, rotate, or revoke it with
64618
65153
  \`overai sql credential list|rotate|revoke\`.
64619
65154
 
65155
+ ## From a script
65156
+
65157
+ - The CLI is a shell over \`@overaihq/sdk\`. A Node script or a long-running
65158
+ program uses the package directly: \`createClient({ baseUrl, token: patToken(secret, baseUrl) })\`
65159
+ gives \`client.fn(id).call()\`, \`client.tableSpace(id).sql()\`,
65160
+ \`client.page(id).save()\`/\`.deploy()\`, \`client.workspaces.list()\`, and the
65161
+ rest of what the commands do, with the same errors. \`overai\` stays the right
65162
+ tool at a terminal; the SDK is for code.
65163
+ - \`@overaihq/sdk/env\` exports \`createEnv\`: \`await createEnv(client)\` is the \`env\` object function
65164
+ code already uses, outside the platform. Everything HTTP-backed is there
65165
+ (\`env.FNxxx.call\`, \`env.TBSxxx.sql\`, \`env.DFxxx.read\`, \`env.STORAGE\`,
65166
+ \`env.AI\`); \`env.BROWSER\` and \`env.CALLER\` exist only inside a function.
65167
+
64620
65168
  ## Reporting platform problems
64621
65169
 
64622
65170
  When broken or unexplained platform behavior blocks progress, send a detailed
@@ -65015,7 +65563,7 @@ Users who prefer their own editor or an AI coding agent (Claude Code, Cursor) ca
65015
65563
 
65016
65564
  Clone includes accessible, non-archived descendants under \`workspaces/<slug>.<workspaceId>/\`, each with its own manifest and overview. Commands use the nearest workspace folder: change into a child to work there. Add \`--recursive\` to status/diff/pull/push to include the live accessible subtree; recursive pull clones new children and matches existing folders by workspace ID. Recursive push saves only and rejects --deploy; deploy/restore remain single-workspace. Conflicts or failures exit nonzero while preserving local edits and successful work. Unavailable or moved-out workspaces stay on disk and are skipped. Local file moves do not move resources between workspaces in OverAI.
65017
65565
 
65018
- The loop is git-shaped: \`overai clone <workspaceId>\` writes the workspace overview to \`WORKSPACE.md\` and materializes automations, functions, web pages, printable pages, and agents as local files; \`status\`/\`diff\`/\`pull\` track local resource edits against remote drift; \`push -m "why"\` creates new versions and \`deploy\` makes them live \u2014 **pushing alone changes nothing users see**. \`overai create <type>\` creates any current resource, while \`resource show|update <id>\` handles mutable metadata. \`workspace show [workspaceId]\` reads the current overview, and \`workspace users [workspaceId]\` lists effective, direct, and inherited access; inside a clone the id is optional. Resource-specific commands invoke functions and agents, trigger automations, run SQL, append/query streams, upload/download files, configure integrations, render printable pages, and pair or use runners. \`overai review\` validates changed resources before a push, and \`overai best-practices\` serves these same doc topics, plus the CLI's own workflow guide, to the local agent.
65566
+ The loop is git-shaped: \`overai clone <workspaceId>\` writes the workspace overview to \`WORKSPACE.md\` and materializes automations, functions, web pages, printable pages, and agents as local files (\`.overai/\` holds sync state and is ignored by git); \`status\`/\`diff\`/\`pull\` track local resource edits against remote drift; \`push -m "why"\` creates new versions and \`deploy\` makes them live \u2014 **pushing alone changes nothing users see**. \`overai create <type>\` creates any current resource, while \`resource show|update <id>\` handles mutable metadata. \`workspace show [workspaceId]\` reads the current overview, and \`workspace users [workspaceId]\` lists effective, direct, and inherited access; inside a clone the id is optional. Resource-specific commands invoke functions and agents, trigger automations, run SQL, append/query streams, upload/download files, configure integrations, render printable pages, and pair or use runners. \`overai review\` validates changed resources before a push, and \`overai best-practices\` serves these same doc topics, plus the CLI's own workflow guide, to the local agent.
65019
65567
 
65020
65568
  Point users here when they ask about editing outside the browser, using a local AI coding agent, version-controlling a workspace, or scripting against the platform. Everything the CLI does is also doable in the Architect \u2014 it is an alternative surface, not a required one. Full command list: \`overai help\`; the user-facing guide is the help center article at \`/<orgId>/help/cli\`.
65021
65569
  `
@@ -65237,6 +65785,10 @@ Two optional JSON Schemas declare the function's contract:
65237
65785
 
65238
65786
  The deployed version's schemas type \`env.FNxxx.call()\` everywhere authors call this function: the envelope's \`body\` member types the call body, and the output schema types \`res.json()\`. Declare the output schema whenever the response shape is stable \u2014 callers get autocomplete and typo-catching lint instead of \`any\` \u2014 and keep both schemas in sync with what the code actually reads and returns. They describe the base path; a router's sub-paths are outside the declared contract.
65239
65787
 
65788
+ Autocomplete and deployed-schema types cover functions owned by or shared into this workspace. A manually named \`env.FNxxx\` (or \`env.fnxxx\`) outside that list has a generic call type, not the target's schema. Static unlisted references warn through \`architect/unlisted-function\`; an intentional cross-workspace call may be valid, but verify the ID and the deployed caller access against the actual caller. Do not claim access was verified without checking, change sharing or caller access just to silence the warning, or invoke a side-effecting function merely to test it. After verification, acknowledge a known reference with \`// eslint-disable-next-line architect/unlisted-function -- <verified reason>\` directly above it. Give a concrete reason; avoid casts, \`@ts-ignore\`, and blanket suppressions. Dynamic IDs are outside this static check.
65789
+
65790
+ If the warning gives a listed function's spelling, use that spelling instead of suppressing it; different casing loses its deployed-schema types.
65791
+
65240
65792
  Lint enforces the output side: when an output schema is declared, a handler whose return value doesn't match it gets a lint error (both on code edits and on schema edits). Returning the plain JSON value (the runtime wraps it in a 200 response) keeps the check active; a hand-built \`Response\` passes unchecked because its payload is invisible to the type checker. Enum values are checked as their base type (\`string\`, not the exact members), and \`null\`/missing-return code paths are not caught \u2014 verify those yourself.
65241
65793
 
65242
65794
  Callers can also name the declared shapes: the deployed schemas generate \`Env.FNxxx.Input\` (the call body) and \`Env.FNxxx.Output\` (the response) for annotating payloads and results.
@@ -65266,6 +65818,12 @@ Built-in variables:
65266
65818
  - \`env.CALLER\` (verified JWT claims from the request, or null \u2014 useful for identifying the calling user from a web page)
65267
65819
  - Any custom secrets/environment variables configured on the function
65268
65820
 
65821
+ The same \`env\` is importable outside the platform: \`import { createEnv } from "@overaihq/sdk/env"\` and \`const env = await createEnv(client)\`
65822
+ gives a Node script every HTTP-backed member above with the same names and
65823
+ shapes. \`env.BROWSER\` and \`env.CALLER\` exist only inside a function, and
65824
+ \`env.TBSxxx.sql\` there returns \`{ rows, rowCount }\` over HTTP instead of a
65825
+ direct database connection.
65826
+
65269
65827
  ## AI Access
65270
65828
 
65271
65829
  Generate text or structured objects using LLMs:
@@ -66055,8 +66613,6 @@ Saves reject a stale editing base without discarding your draft. Incorporate the
66055
66613
 
66056
66614
  \`\`\`typescript
66057
66615
  declare global {
66058
- const filterData: { id: string; attributes: { id: string; attributeId: string; value: string; }[]; }[] | null;
66059
- const dataFile: Record<string, unknown> | null;
66060
66616
  const searchParams: Record<string, unknown>;
66061
66617
  const setSearchParam: { (key: string, value: string | null): void; (params: Record<string, string | null>): void }; // pass null to delete a param; pass an object to set/delete several in one URL update
66062
66618
  const navigateToPage: (webPageId: string, queryParameters?: Record<string, string>, options?: { newTab?: boolean }) => void; // { newTab: true } opens the page in a new browser tab \u2014 use it instead of hand-building URLs with window.open
@@ -66070,6 +66626,8 @@ declare global {
66070
66626
  /** @deprecated Use useLLMText, useLLMObject, or useLLMChat instead. */
66071
66627
  const llmCompletion: (message: string, systemPrompt?: string, model?: string) => Promise<Record<string, unknown>>;
66072
66628
  const callFunction: (functionId: string, options?: { body?: unknown; method?: string; query?: Record<string, string>; path?: string }) => Promise<Response>;
66629
+ // Start a deployed automation. \`inputs\` maps onto its event node's declared outputs (e.g. { fileUrl } for a file-upload trigger \u2014 upload with env.STORAGE.upload first). Throws on non-2xx.
66630
+ const startAutomation: (automationId: string, inputs?: Record<string, unknown>) => Promise<{ automationRunId: string }>;
66073
66631
 
66074
66632
  // Imperative AI functions
66075
66633
  // Prefer stream: false for one-shot generateText calls \u2014 errors (invalid PDF, prompt too long, rate limit) come back as HTTP errors with a friendly message instead of an empty body. Only stream when you wire onChunk for progressive UI.
@@ -66086,20 +66644,10 @@ declare global {
66086
66644
  const callAgentGenerateObject: (agentId: string, message: string, schema: Record<string, unknown>) => Promise<{ object: unknown; model: string; finishReason: string; usage: { promptTokens: number; completionTokens: number; totalTokens: number }; toolResults: { toolName: string; input: unknown; output: unknown; error: unknown }[] }>;
66087
66645
  const callAgentChat: (agentId: string, messages: { role: string; parts: { type: string; text?: string }[] }[], options?: { conversationId?: string; onChunk?: (accumulated: string) => void }) => Promise<string>;
66088
66646
 
66089
- // Agent Hook \u2014 multi-turn streaming chat with an OverAI Agent.
66090
- // Every turn is persisted server-side for a signed-in viewer. Pass conversationId
66091
- // to resume a past thread (its full transcript, tool calls included, loads on
66092
- // mount). Omit it to start fresh \u2014 a conversation is created on the first send and
66093
- // returned as conversationId. messages[].parts holds full UI parts for loaded turns;
66094
- // the live-streaming turn carries text only.
66095
- // On a PUBLIC page there is no viewer identity: chat works, but the transcript
66096
- // lives in the hook for the tab's life, conversationId stays null, and the
66097
- // conversation helpers below throw. See "Public pages" below.
66647
+ // Agent Hook \u2014 multi-turn streaming chat with an OverAI Agent; persistence and resume are explained below the block. messages[].parts holds full UI parts for loaded turns; the live-streaming turn carries text only.
66098
66648
  const useAgentChat: (agentId: string, options?: { conversationId?: string }) => { messages: { id?: string; role: string; text: string; parts?: { type: string; text?: string; toolName?: string; toolCallId?: string; state?: string; input?: unknown; output?: unknown }[] }[]; sendMessage: (text: string) => void; clearMessages: () => void; loading: boolean; error: string | null; conversationId: string | null };
66099
66649
 
66100
- // Agent conversation management \u2014 list/read/manage a signed-in user's chat threads
66101
- // with an agent. Unavailable on public pages (no identity owns a thread):
66102
- // useAgentConversations returns an empty list, the rest throw.
66650
+ // Agent conversation management \u2014 list/read/manage a signed-in user's chat threads with an agent.
66103
66651
  const useAgentConversations: (agentId: string) => { conversations: { id: string; title: string; updatedAt: string }[]; loading: boolean; error: string | null; refresh: () => Promise<void> };
66104
66652
  const listAgentConversations: (agentId: string) => Promise<{ id: string; title: string; updatedAt: string }[]>;
66105
66653
  const getAgentConversationMessages: (conversationId: string) => Promise<{ id?: string; role: string; text: string; parts?: { type: string; text?: string }[] }[]>;
@@ -66121,9 +66669,12 @@ declare global {
66121
66669
  /** Escape hatch for dynamic SQL. Caller is responsible for parameterizing. */
66122
66670
  unsafe: <Row = any>(query: string, params?: unknown[]) => Promise<{ rows: Row[]; rowCount: number }>;
66123
66671
  };
66672
+ /** Several statements that must all land or none do. Pass lazy \`sql\` fragments; resolves to { results, executionId, durationMs } with results[i] = { rows, rowCount }. Any failure rolls everything back and throws. */
66673
+ transaction: (statements: readonly unknown[]) => Promise<{ results: { rows: Record<string, unknown>[]; rowCount: number }[]; executionId: string; durationMs: number }>;
66124
66674
  /** @deprecated Use \`sql\` \u2014 it auto-parameterizes interpolated values and returns rows directly. query returns camelCased columns (sql returns snake_case); do not mix the two. */
66125
66675
  query: (sql: TemplateStringsArray, ...values: unknown[]) => Promise<Response>;
66126
66676
  };
66677
+ [automationId: \`AT\${string}\`]: { start: (inputs?: Record<string, unknown>) => Promise<{ automationRunId: string }> };
66127
66678
  /** \`path\` routes to a sub-path inside the function (e.g. "/features/123") for functions that ship their own router; omit it to hit the base path. Method still defaults to POST. */
66128
66679
  [functionId: \`FN\${string}\`]: { call: (options?: { body?: unknown; method?: string; query?: Record<string, string>; path?: string }) => Promise<Response> };
66129
66680
  [agentId: \`AG\${string}\`]: { generateText: (message: string) => Promise<{ text: string; model: string; finishReason: string; usage: { promptTokens: number; completionTokens: number; totalTokens: number }; toolResults: { toolName: string; input: unknown; output: unknown; error: unknown }[] }>; generateObject: (message: string, schema: Record<string, unknown>) => Promise<{ object: unknown; model: string; finishReason: string; usage: { promptTokens: number; completionTokens: number; totalTokens: number }; toolResults: { toolName: string; input: unknown; output: unknown; error: unknown }[] }> };
@@ -66140,8 +66691,6 @@ declare global {
66140
66691
  };
66141
66692
  };
66142
66693
  const React: typeof import("react");
66143
- const useDataFile: () => Record<string, unknown> | null;
66144
- const useFilterData: () => { id: string; attributes: { id: string; attributeId: string; value: string; }[]; }[] | null;
66145
66694
  const useCurrentUser: () => { id: string; email: string; name: string; } | null;
66146
66695
  // useTheme re-renders on the app's light/dark toggle. The page root already carries a \`dark\` class (Tailwind darkMode: "class"), so style with \`dark:\` variants \u2014 e.g. bg-white dark:bg-slate-900 \u2014 and reach for the hook only for JS-driven styling (chart palettes, canvas, map tiles).
66147
66696
  // Two things a light-only page gets wrong in dark mode: a border or divide utility with no color class falls back to Tailwind's default light gray, so \`border-b\` alone stays a bright rule \u2014 pair it, e.g. border-b border-gray-200 dark:border-gray-700. And if the page loads daisyUI, its theme drives the inherited text color, so a hardcoded bg-white left to inherit turns light-on-white; give every surface a dark counterpart.
@@ -66154,19 +66703,29 @@ declare global {
66154
66703
  const useLocalStorageState: <T>(key: string, initialValue: T) => [T, React.Dispatch<React.SetStateAction<T>>];
66155
66704
  // useSessionStorageState is a useState-like hook backed by sessionStorage. Persists for the lifetime of the tab. REQUIRES noSandbox: true in the page config.
66156
66705
  const useSessionStorageState: <T>(key: string, initialValue: T) => [T, React.Dispatch<React.SetStateAction<T>>];
66706
+
66707
+ // Legacy \u2014 populated only by the deprecated dataSourceUrl / filter page settings (see "Legacy data sources" below). New pages use env instead.
66708
+ const dataFile: Record<string, unknown> | null;
66709
+ const useDataFile: () => Record<string, unknown> | null;
66710
+ const filterData: { id: string; attributes: { id: string; attributeId: string; value: string; }[]; }[] | null;
66711
+ const useFilterData: () => { id: string; attributes: { id: string; attributeId: string; value: string; }[]; }[] | null;
66157
66712
  }
66158
66713
  \`\`\`
66159
66714
 
66160
- IMPORTANT: Use the hooks (useDataFile, useFilterData, useSearchParamState) instead of the global variables when you need the component to re-render when data or URL state changes. URL state belongs in search params (via useSearchParamState), not local React state, so links and reloads preserve it.
66715
+ IMPORTANT: Use useSearchParamState instead of the searchParams global when the component must re-render as the URL changes. URL state belongs in search params (via useSearchParamState), not local React state, so links and reloads preserve it.
66161
66716
 
66162
66717
  IMPORTANT: callFunction and env.FNxxx.call return a Response object (like fetch) \u2014 call .json() or .text() to get the data. Date/timestamp values come back as ISO 8601 strings (e.g., "2026-04-06T00:00:00.000Z"). When the function declares input/output schemas, env.FNxxx.call types the body and .json() from them (the deployed version's declaration) \u2014 no annotation needed.
66163
66718
 
66719
+ Function autocomplete and deployed-schema types cover functions owned by or shared into this workspace. A manually named \`env.FNxxx\` or \`env.fnxxx\` outside that list remains a generic call, not a typed discovery of organization functions. A static unlisted reference raises advisory \`architect/unlisted-function\`; dynamic IDs are not checked. Verify the ID and deployed caller access for the page's actual caller before using an intentional cross-workspace reference. Never claim unverified access, change sharing or authorization just to silence the warning, or invoke a side-effecting function merely to test it. Once verified, acknowledge the reference with \`// eslint-disable-next-line architect/unlisted-function -- <verified reason>\` directly above it. Prefer a concrete reason, not casts, \`@ts-ignore\`, or blanket suppression.
66720
+
66721
+ If the warning gives a listed function's spelling, use that spelling instead of suppressing it; different casing loses its deployed-schema types.
66722
+
66164
66723
  The \`env\` proxy matches the API available in Functions and Automations \u2014 the same code works in all three.
66165
66724
 
66166
66725
  For AI in React components, prefer the reactive hooks (useLLMText, useLLMObject, useLLMChat, useAgentChat) \u2014 they re-render as the response streams in. In event handlers or imperative code, call generateText/generateObject (or env.AI.*) directly.
66167
66726
  useAgentChat persists every turn server-side for a signed-in viewer: pass a conversationId to resume a past thread (full transcript, tool calls included, loads automatically), or omit it to start fresh and read the new thread's id off the returned conversationId. useAgentConversations(agentId) backs a history sidebar \u2014 call its refresh() after a first send so the newly created thread appears. listAgentConversations, getAgentConversationMessages, createAgentConversation, renameAgentConversation, and deleteAgentConversation are the imperative equivalents. On a public page none of that persistence exists (see Public pages) \u2014 useAgentChat still streams, holding the transcript in memory.
66168
66727
  All AI functions/hooks accept optional File[] attachments for images/PDFs, and webSearch: true to enable web search.
66169
- Available models: the model ids this workspace can use. Omit \`model\` to use this workspace's default.
66728
+ Available models: read \`availableModelIds\` from the \`getWebPageContext\` data tool. Omit \`model\` to use this workspace's default.
66170
66729
 
66171
66730
  IMPORTANT: Keep generateObject / useLLMObject / agent generateObject schemas simple. Anthropic models reject schemas with more than 16 nullable or union-typed fields (this includes optional fields the AI SDK emits as \`type: [..., "null"]\`). Prefer non-nullable fields, split into multiple calls, or switch to a non-Anthropic model from the list above for larger schemas.
66172
66731
 
@@ -66242,25 +66801,16 @@ const { rows } = await env.TBS123.sql.unsafe(
66242
66801
 
66243
66802
  Pages render UI; they don't perform server-side actions directly. To do anything with side effects beyond table-space SQL (send email, hit a third-party API, post to Slack, etc.), call a Function or trigger an Automation. Table spaces are the exception: read AND write them directly with env.TBSxxx.sql as shown above.
66244
66803
 
66245
- - **Common actions** like sending email, Slack messages, SMS, or HTTP calls are first-class Automation nodes. \`SEND_EMAIL\` and \`HTTP\` need no integration; \`SLACK_SEND_MESSAGE\` and \`TWILIO_SEND_SMS\` require their respective integration to be configured. Build a webhook-triggered automation and trigger it from the page (snippet below).
66804
+ - **Common actions** like sending email, Slack messages, SMS, or HTTP calls are first-class Automation nodes. \`SEND_EMAIL\` and \`HTTP\` need no integration; \`SLACK_SEND_MESSAGE\` and \`TWILIO_SEND_SMS\` require their respective integration to be configured. Build the automation, deploy it, and start it from the page with \`env.ATxxx.start(inputs)\` (snippet below) \u2014 never hand-build its webhook URL.
66246
66805
  - **Custom server logic** belongs in a Function. Call it from the page via \`callFunction('FN123', { body: {...} })\` or \`env.FN123.call({ body: {...} })\`. A Function that ships its own router serves many routes \u2014 reach one with \`path\`: \`env.FN123.call({ method: 'GET', path: '/features/123' })\`. Omit \`path\` for the base path; the method defaults to POST either way.
66247
66806
  - **Action runners** (\`env.ACTIONS.run\`) are server-only \u2014 not available on a page. To run an action runner action from a page, put the \`env.ACTIONS.run(...)\` call in a Function and invoke that Function from the page.
66248
66807
  - **Email** (\`env.EMAIL.send\`) is server-only for the same reason. Put the \`env.EMAIL.send(...)\` call in a Function and invoke that Function from the page.
66249
66808
  - **Integrations** are credentials/config consumed *by* automation nodes or functions \u2014 they are not available directly in the page context. Only reach for an Integration when the work requires authenticated access to a specific third-party service.
66250
66809
 
66251
66810
  \`\`\`typescript
66252
- // Trigger a webhook-triggered automation from a page. The full webhook URL
66253
- // is shown in the automation editor \u2014 webhook automations are unauthenticated,
66254
- // so the URL itself is the credential.
66255
- const response = await fetch(
66256
- 'https://api.overai.com/v1/automation/webhook/ORG123/AT123',
66257
- {
66258
- method: 'POST',
66259
- headers: { 'Content-Type': 'application/json' },
66260
- body: JSON.stringify({ recipient: 'user@example.com' }),
66261
- },
66262
- );
66263
- const { automationRunId } = await response.json();
66811
+ // Start a deployed automation. inputs maps onto its event node's declared
66812
+ // outputs; for a file-upload trigger, upload first and pass the url.
66813
+ const { automationRunId } = await env.AT123.start({ recipient: 'user@example.com' });
66264
66814
 
66265
66815
  // To use an integration's credentials, call a Function that reads the integration config server-side.
66266
66816
  const config = await callFunction('FN123', { body: { integrationId: 'IN123' } });
@@ -66300,17 +66850,13 @@ Page settings (\`authorizationMode\`, \`noSandbox\`, \`hideTopBar\`, \`runtime\`
66300
66850
 
66301
66851
  \`runtime: "v2"\` serves the page from its own origin instead of the sandboxed srcdoc iframe: separate renderer process, real localStorage/sessionStorage and cookies with no noSandbox flag needed. It is versioned like every other setting \u2014 save and deploy to publish. Public (/public/:id) links always use v1 regardless of the toggle. Only v2 reaches browser devices: Web Serial, WebUSB and WebHID always work there; camera, microphone, geolocation, display-capture, fullscreen and screen-wake-lock work once listed in \`permissions\`.
66302
66852
 
66303
- **\`dataSourceUrl\` and \`filter\` are legacy.** They populate the \`data\` and \`items\` globals, frozen at render start; \`filter\` reads the old item/attribute model, which builders can no longer create or manage. For new pages use \`env\` instead \u2014 \`env.TBSxxx.sql\` for tabular data, \`env.DFxxx\` for data files. That path can depend on search params, re-query on demand, and report its own errors. Don't rewrite a working page just to migrate it.
66304
-
66305
- \`dataSourceUrl\` is fetched **verbatim** and its response becomes the \`data\` global. It is a plain static URL, not a template:
66853
+ ### Legacy data sources: \`dataSourceUrl\`, \`filter\`, \`data\`, \`items\`
66306
66854
 
66307
- - It does **not** substitute search params. Neither \`{x}\` nor Liquid \`{{ x }}\` is interpolated \u2014 the URL is fetched exactly as written. (\`filter\` does support Liquid; \`dataSourceUrl\` does not.)
66308
- - If your data depends on a search param or anything else known only at render time, don't use \`dataSourceUrl\` \u2014 fetch it in the page with \`useEffect\` + \`env\`/\`fetch\`, which is the normal path anyway.
66309
- - To clear it, pass JSON \`null\`. The string \`"null"\` is a four-character URL, not an empty value, and it fails at fetch time.
66855
+ These populate the \`data\`/\`items\` globals once, at render start, from the old item/attribute model that builders can no longer create. New pages use \`env\` (\`env.TBSxxx.sql\`, \`env.DFxxx\`), which can depend on search params, re-query on demand and report its own errors. Don't rewrite a working page just to migrate it. If you must touch them:
66310
66856
 
66311
- \`filter\`, unlike \`dataSourceUrl\`, **does** evaluate Liquid \u2014 in every string value it holds, against \`now\` (a Date rounded down to the minute, so it doesn't move mid-session) plus every search param by name. Use \`{{ now | minus: 1209600000 | toISOString }}\` for a relative date, \`{{ since }}\` for \`?since=\`. A template that won't evaluate, or that evaluates to nothing because its variable isn't on the URL, fails the whole page rather than quietly querying on an empty value.
66312
-
66313
- A templated filter is **not** an access boundary. On a public or portal page the visitor supplies the search params, so \`{"orgUnit": {"_eq": "{{ tenant }}"}}\` reads like tenant scoping but is editable from the URL bar. Scope with the workspace's own access, not with a filter.
66857
+ - \`dataSourceUrl\` is fetched verbatim \u2014 no \`{x}\` or Liquid interpolation. Anything that depends on a search param belongs in a page-side fetch instead. Clear it with JSON \`null\`; the string \`"null"\` is a four-character URL.
66858
+ - \`filter\` evaluates Liquid in every string value against \`now\` (rounded to the minute) and each search param by name, e.g. \`{{ now | minus: 1209600000 | toISOString }}\` or \`{{ since }}\`. A template whose variable is missing from the URL fails the whole page rather than querying on an empty value.
66859
+ - A templated filter is **not** an access boundary: on a public or portal page the visitor controls the search params, so \`{{ tenant }}\` scoping is editable from the URL bar. Scope with the workspace's own access.
66314
66860
 
66315
66861
  ## Critical Rules
66316
66862
 
@@ -66323,13 +66869,13 @@ A templated filter is **not** an access boundary. On a public or portal page the
66323
66869
  | \`useSessionStorageState(key, init)\` | Single-tab session state (wizard progress, scroll restoration) | Reload (same tab); cleared on tab close | **Requires \`noSandbox: true\`** |
66324
66870
  | \`useState\` | Truly ephemeral UI (hover, animation step, in-flight form field before submit) | Nothing | Lost on every navigation |
66325
66871
 
66326
- When the page uses \`useLocalStorageState\` or \`useSessionStorageState\`, set \`noSandbox: true\` in the page config first \u2014 the hooks throw a clear error otherwise.
66872
+ When the page uses \`useLocalStorageState\` or \`useSessionStorageState\` on the default v1 runtime, set \`noSandbox: true\` in the page config first \u2014 the hooks throw a clear error otherwise. A \`runtime: "v2"\` page needs no flag.
66327
66873
 
66328
66874
  2. **DO NOT import React or hooks.** React, useState, useEffect, useRef, useMemo, useCallback, and useReducer are all globally available. Importing them will cause errors.
66329
66875
  3. **The main component MUST be named \`App\` and MUST NOT be exported.** Write \`function App() { ... }\` \u2014 no \`export\`.
66330
66876
  4. **Use Tailwind CSS for styling.** It's built in.
66331
66877
  5. **Import external libraries from esm.sh.** Example: \`import confetti from "https://esm.sh/canvas-confetti@1.9.3"\`. Imports must be absolute https URLs \u2014 the sandbox cannot resolve bare or relative specifiers.
66332
- 6. **Sandbox restrictions.** The page runs in a sandboxed iframe (allow-scripts only). Raw \`localStorage\`, \`sessionStorage\`, cookies, alert/confirm/prompt, and form submissions are blocked. Use \`useLocalStorageState\` / \`useSessionStorageState\` for storage (both require disabling the sandbox \u2014 see Rule 1). Set \`noSandbox: true\` in the page config to disable the sandbox when any of these are needed.
66878
+ 6. **Sandbox restrictions (v1 runtime).** By default the page runs in a sandboxed iframe (allow-scripts only). Raw \`localStorage\`, \`sessionStorage\`, cookies, alert/confirm/prompt, and form submissions are blocked. Use \`useLocalStorageState\` / \`useSessionStorageState\` for storage (both require disabling the sandbox \u2014 see Rule 1). Set \`noSandbox: true\` to lift the sandbox, or \`runtime: "v2"\` (see Web Page Configuration) when the page also needs its own origin or browser devices.
66333
66879
  7. **Read code before editing.** Reading a large page's config returns its outline instead of the code \u2014 then read only the blocks or line ranges you are about to change. Edits return the changed lines, numbered, so don't re-read to verify. Prefer small, targeted edits \u2014 insert or search-replace \u2014 over rewriting a whole block.
66334
66880
  8. **Decompose large pages.** When a page exceeds ~200 lines, break it into multiple top-level function components (e.g., \`function DataTable() { ... }\` used inside \`function App() { ... }\`), so each can be read and edited on its own.
66335
66881
  9. **Add TSDoc comments to every top-level declaration.** Write a \`/** brief description */\` comment above each function, component, and constant. Code outlines show these comments, giving structural context without reading the full code. Always add or update these when creating or modifying a block.
@@ -66340,6 +66886,46 @@ A templated filter is **not** an access boundary. On a public or portal page the
66340
66886
  12. **Sanity-check chart math with console.log.** You cannot see the rendered output. When building or debugging visualizations, log intermediate values (scales, domains, ranges, computed positions, angles) and verify they make sense before saving. For example: \`console.log("gauge", { min, max, value, fraction, startAngle, endAngle, valueAngle })\`. Remove logs once the chart is working.
66341
66887
  13. **Rebuild imported HTML natively \u2014 never embed it.** When the user brings HTML from somewhere else (an exported dashboard, a design mockup, a page from another tool), recreate it as a native page: an \`App\` component, Tailwind, d3 for charts. Pay close attention to preserving the original styling \u2014 colors, spacing, type scale, layout. Do NOT load a foreign HTML file into a \`srcDoc\` iframe or inject it into the DOM. Embedded HTML is opaque to code outlines and reachable only by blind string patches that silently do nothing when the source drifts \u2014 so every later request turns into guesswork. Port it once and every edit after that is a normal edit. (A Liquid printable page is the exception: Liquid is HTML templating, so imported markup becomes the template body directly.)
66342
66888
 
66889
+ ## Best Practices
66890
+
66891
+ **Decide the page's purpose first, then design for it.** A page answers one question; several questions become tabs or separate pages. Most pages are one of three kinds, and each wants a different shape:
66892
+
66893
+ - **Single-flow page** \u2014 one task, start to finish (submit a request, review an item, check in). Keep it simple: one column, one primary action per screen, generous spacing. Think consumer app, not admin panel.
66894
+ - **KPI dashboard** \u2014 a few measures that matter, seen from several angles (trend, breakdown, against target). Users don't want to read a number, they want to feel it: a dense, color-accented table or a chart built for the specific question beats a row of big-number tiles. Add headline tiles only when asked.
66895
+ - **Command center** \u2014 operators who live in the page and need to act fast. Information density is the point: compact rows, inline status, filters and actions within reach, no decorative whitespace.
66896
+
66897
+ Name the kind to the user when you propose a design; if the request mixes kinds, split it rather than compromising all three.
66898
+
66899
+ **Build a clear hierarchy.** Size is only one tool. Group related elements and separate unrelated ones with spacing, borders or dividers; order sections by importance, with the thing the user came for first; distinguish primary actions from secondary ones; mute labels and metadata so values stand out. The visible structure should mirror the data model underneath, so relationships between concepts are obvious. A reader should see what matters before reading a word.
66900
+
66901
+ **Spend pixels on data, not decoration.** Don't repeat a label on every row or card when a column header or section title carries it. Use color sparingly and for meaning: if problems are red, tune the thresholds so only real problems are, or the signal is lost. No emojis. Add a tooltip where a value's origin or calculation isn't obvious. Disable a control the user can't use right now instead of hiding it, so the page teaches what it can do.
66902
+
66903
+ **Choose the layout deliberately.**
66904
+
66905
+ - A scrolling document, or a command center where panels scroll independently inside a fixed viewport? For the latter, use CSS grid over flex, fill the viewport, and don't cap the page with a top-level max-width.
66906
+ - Tables for rows the user compares; cards or grids for non-uniform items. Size each column for its content and decide which wrap and which truncate.
66907
+ - Drill in with a drawer when the surrounding context should stay visible, a large modal when the user needs focus. Make the background inert while a modal is open.
66908
+ - Decide whether the page must work on a small screen; not every page does, but check the ones that will be opened on a phone.
66909
+ - Style for both themes: the page root carries a \`dark\` class, so pair every surface and border with a \`dark:\` variant (see \`useTheme\` above).
66910
+
66911
+ **Represent data accurately.** A page that looks right but misreports the data is worse than one that shows an error. Common failures:
66912
+
66913
+ - **A query \`LIMIT\` is not a count.** Loading \`LIMIT 1000\` and labeling it "1000 items" misreports every larger table. Run a separate \`count(*)\` for totals, and label a truncated list as such ("Showing 1,000 of 14,212").
66914
+ - **Offer "Load more" when there is more.** Prefer a subtle "Load more" control over numbered pagination; either way the user must be able to reach every row.
66915
+ - **Dates are not timestamps \u2014 check the time zone before you format.** A \`date\` column (a calendar day, no time) arrives from \`env.TBSxxx.sql\` as a JS \`Date\` at 00:00 UTC, so \`toLocaleDateString()\` in any zone west of UTC shows the previous day. Format a day from its UTC fields (\`d.toISOString().slice(0, 10)\` or \`Intl.DateTimeFormat(undefined, { timeZone: "UTC", dateStyle: "medium" })\`), and never pass it through local-time math. A \`timestamptz\` is a real instant: format it in the viewer's zone and show the zone (\`timeZoneName: "short"\`) when readers may be in different places. A \`timestamp\` without zone is ambiguous \u2014 ask which zone it was written in rather than guessing. Function responses carry dates as ISO strings; the same rules apply after \`new Date(iso)\`. Day-boundary filters (\`today\`, \`this week\`) must be computed in the business's zone, in SQL via \`AT TIME ZONE\`, not in the browser's. Tell the user which columns are days and which are instants, and label the zone in the UI.
66916
+ - **Never fall back to a value that hides a data problem.** \`?? 0\`, \`?? "N/A"\`, \`?? []\` make a broken query or a missing column look like a legitimate result. Show an explicit empty or error state, and when a value is unexpectedly null, surface it (a dash, an "unknown" badge) rather than inventing a default.
66917
+
66918
+ **Embed PDFs with \`<object>\` on a v2 page.** When \`runtime: "v2"\`, render a PDF with \`<object data={url} type="application/pdf" />\` rather than an \`<iframe>\`.
66919
+
66920
+ **Link stored files through the app host.** Storage URLs are persisted as \`https://api.<host>/v1/storage/...\`, where the browser holds no session. Before rendering one as a link, embed or download, rewrite it to the app origin's proxy, \`https://app.<host>/api/v1/storage/...\`:
66921
+
66922
+ \`\`\`typescript
66923
+ const browserStorageUrl = (url: string) =>
66924
+ url.replace(/^https:\\/\\/api\\.([^/]+)\\/v1\\/storage\\//, "https://app.$1/api/v1/storage/");
66925
+ \`\`\`
66926
+
66927
+ The edge also redirects a credential-less api.* read there, so an unrewritten link degrades to an extra hop rather than failing. The proxy authenticates with the signed-in session cookie, so it works for workspace members only. On a public page the visitor has no session: use \`presignFile\` for links there. Page code calling \`fetchFile\`, \`env.STORAGE.fetch\` or \`presignFile\` is unaffected either way \u2014 those carry their own credentials.
66928
+
66343
66929
  ## Known-Good Libraries
66344
66930
 
66345
66931
  These libraries are tested and work from \`https://esm.sh/...\`. Do NOT import React \u2014 it's already global.
@@ -66349,10 +66935,15 @@ These libraries are tested and work from \`https://esm.sh/...\`. Do NOT import R
66349
66935
  - **date-fns** \u2014 \`import { format, parseISO } from "https://esm.sh/date-fns@4"\` \u2014 Date formatting and manipulation.
66350
66936
  - **papaparse** \u2014 \`import Papa from "https://esm.sh/papaparse@5"\` \u2014 CSV parsing.
66351
66937
 
66352
- ### Do NOT use these libraries (broken ESM imports):
66938
+ ### Do NOT use libraries that depend on React
66939
+
66940
+ An esm.sh build of a React library imports its own React (\`import "/react@>=16.8"\`), a second copy beside the page's global one, so every hook inside it fails with "Invalid hook call". The page has no import map to redirect that specifier, so there is no workaround. This rules out, among others:
66941
+
66353
66942
  - **react-resize-detector** \u2014 Use a \`ResizeObserver\` hook instead (see pattern below).
66354
66943
  - **@tanstack/react-table** \u2014 Build tables with plain \`<table>\` + Tailwind + \`useState\` for sorting/filtering/pagination.
66355
- - **recharts, visx** \u2014 Use d3 directly instead.
66944
+ - **recharts, visx, and any other React chart or component kit** \u2014 Use d3 directly instead.
66945
+
66946
+ React-free libraries (d3, date-fns, papaparse) import fine.
66356
66947
 
66357
66948
  ### Responsive chart pattern
66358
66949
 
@@ -66683,38 +67274,17 @@ var runReview = async () => {
66683
67274
  process.stdout.write("Nothing modified locally \u2014 nothing to review.\n");
66684
67275
  return;
66685
67276
  }
67277
+ const client = cliClient();
66686
67278
  for (const target of lintTargets) {
66687
67279
  try {
66688
- const response = await v2Fetch("/v2/architect/lint", {
66689
- method: "POST",
66690
- headers: { "Content-Type": "application/json" },
66691
- body: JSON.stringify({
66692
- code: target.code,
66693
- surface: target.surface,
66694
- workspaceId: manifest.workspaceId,
66695
- functionOutputSchema: target.functionOutputSchema
66696
- })
67280
+ const response = await client.lint({
67281
+ code: target.code,
67282
+ surface: target.surface,
67283
+ workspaceId: manifest.workspaceId,
67284
+ functionOutputSchema: target.functionOutputSchema
66697
67285
  });
66698
- const responseText = await response.text();
66699
- if (!response.ok) {
66700
- throw new Error(
66701
- `${target.label}: HTTP ${String(response.status)}${responseText ? ` \u2014 ${responseText}` : ""}`
66702
- );
66703
- }
66704
- const parsed = lintResponseSchema.safeParse(
66705
- (() => {
66706
- try {
66707
- return JSON.parse(responseText);
66708
- } catch {
66709
- return null;
66710
- }
66711
- })()
66712
- );
66713
- if (!parsed.success) {
66714
- throw new Error(`${target.label}: invalid response from lint service`);
66715
- }
66716
67286
  findings2.push(
66717
- ...parsed.data.diagnostics.map(
67287
+ ...response.diagnostics.map(
66718
67288
  (diagnostic) => `${target.label}:${String(diagnostic.line)}:${String(diagnostic.column)}: ${diagnostic.severity}: ${diagnostic.message}${diagnostic.ruleId == null ? "" : ` (${diagnostic.ruleId})`}`
66719
67289
  )
66720
67290
  );
@@ -66749,23 +67319,6 @@ var runReview = async () => {
66749
67319
  import { createInterface as createInterface3 } from "readline";
66750
67320
 
66751
67321
  // src/commands/sqlCredential.ts
66752
- var connectionString = async (tableSpaceId, secret) => {
66753
- const response = await v2Fetch("/v2/cluster-info");
66754
- const body = await response.json();
66755
- return `postgresql://overai:${encodeURIComponent(secret)}@${body.postgres.host}:${String(body.postgres.port)}/${tableSpaceId}?sslmode=${body.postgres.sslMode}${body.postgres.sslMode === "verify-full" ? "&sslrootcert=system" : ""}`;
66756
- };
66757
- var credentialTableSpaceId = async (credentialId) => {
66758
- const data = await gqlRequest(
66759
- `query TableSpaceCredential($id: String!) {
66760
- tableSpaceCredential(id: $id) { tableSpaceId }
66761
- }`,
66762
- { id: credentialId }
66763
- );
66764
- if (!data.tableSpaceCredential) {
66765
- throw new Error("Credential not found or you lack access to it");
66766
- }
66767
- return data.tableSpaceCredential.tableSpaceId;
66768
- };
66769
67322
  var runSqlCredential = async (args) => {
66770
67323
  const [action, id] = args;
66771
67324
  if (!action || !id) {
@@ -66785,67 +67338,31 @@ var runSqlCredential = async (args) => {
66785
67338
  if (!args.includes("--never") && (!Number.isInteger(days) || days < 1)) {
66786
67339
  throw new Error("--expires-in must be a positive whole number of days");
66787
67340
  }
66788
- const data = await gqlRequest(
66789
- `mutation CreateTableSpaceCredential($tableSpaceId: String!, $name: String!, $validUntil: Datetime) {
66790
- createTableSpaceCredential(input: { tableSpaceId: $tableSpaceId, name: $name, validUntil: $validUntil }) { result }
66791
- }`,
66792
- {
66793
- tableSpaceId,
66794
- name: name2,
66795
- validUntil: args.includes("--never") ? null : new Date(Date.now() + days * 864e5).toISOString()
66796
- }
66797
- );
66798
- const result = data.createTableSpaceCredential?.result;
66799
- if (!result) throw new Error("The server did not return a credential");
66800
- process.stdout.write(
66801
- `${await connectionString(tableSpaceId, result.secret)}
66802
- `
66803
- );
67341
+ const credential = await cliClient().tableSpace(tableSpaceId).credentials.create({
67342
+ name: name2,
67343
+ validUntil: args.includes("--never") ? null : new Date(Date.now() + days * 864e5)
67344
+ });
67345
+ process.stdout.write(`${credential.connectionString}
67346
+ `);
66804
67347
  return;
66805
67348
  }
66806
67349
  if (action === "list") {
66807
67350
  const tableSpaceId = id;
66808
- const data = await gqlRequest(
66809
- `query TableSpaceCredentials($tableSpaceId: String!) {
66810
- tableSpaceCredentials(condition: { tableSpaceId: $tableSpaceId }) {
66811
- nodes { id name validUntil lastUsedAt createdAt }
66812
- }
66813
- }`,
66814
- { tableSpaceId }
66815
- );
66816
67351
  printResult(
66817
- data.tableSpaceCredentials?.nodes ?? [],
67352
+ await cliClient().tableSpace(tableSpaceId).credentials.list(),
66818
67353
  resolveFormat(flagValue(args, "-o", "--output"))
66819
67354
  );
66820
67355
  return;
66821
67356
  }
66822
67357
  if (action === "rotate") {
66823
- const credentialId = id;
66824
- const tableSpaceId = await credentialTableSpaceId(credentialId);
66825
- const data = await gqlRequest(
66826
- `mutation RotateTableSpaceCredential($credentialId: String!) {
66827
- rotateTableSpaceCredential(input: { credentialId: $credentialId }) { result }
66828
- }`,
66829
- { credentialId }
66830
- );
66831
- const secret = data.rotateTableSpaceCredential?.result;
66832
- if (!secret) throw new Error("The server did not return a credential");
66833
- process.stdout.write(`${await connectionString(tableSpaceId, secret)}
67358
+ const { connectionString: connectionString2 } = await cliClient().tableSpaceCredential(id).rotate();
67359
+ process.stdout.write(`${connectionString2}
66834
67360
  `);
66835
67361
  return;
66836
67362
  }
66837
67363
  if (action === "revoke") {
66838
- const credentialId = id;
66839
- await credentialTableSpaceId(credentialId);
66840
- await gqlRequest(
66841
- `mutation RevokeTableSpaceCredential($id: String!, $validUntil: Datetime!) {
66842
- updateTableSpaceCredential(input: { id: $id, patch: { validUntil: $validUntil } }) {
66843
- tableSpaceCredential { id }
66844
- }
66845
- }`,
66846
- { id: credentialId, validUntil: (/* @__PURE__ */ new Date()).toISOString() }
66847
- );
66848
- process.stdout.write(`${credentialId} revoked
67364
+ await cliClient().tableSpaceCredential(id).revoke();
67365
+ process.stdout.write(`${id} revoked
66849
67366
  `);
66850
67367
  return;
66851
67368
  }
@@ -66853,32 +67370,12 @@ var runSqlCredential = async (args) => {
66853
67370
  };
66854
67371
 
66855
67372
  // src/commands/sql.ts
66856
- var runQuery = async (organizationId, tableSpaceId, query, params) => {
66857
- const res = await v2Fetch(
66858
- `/v2/table-spaces/${organizationId}/${tableSpaceId}/run`,
66859
- {
66860
- method: "POST",
66861
- headers: { "Content-Type": "application/json" },
66862
- body: JSON.stringify(
66863
- params == null ? { query, ignoreCamelCase: true } : { unsafe: { query, params } }
66864
- )
66865
- }
66866
- );
66867
- const raw2 = await res.text();
66868
- const body = (() => {
66869
- try {
66870
- return JSON.parse(raw2);
66871
- } catch {
66872
- return null;
66873
- }
66874
- })();
66875
- if (!res.ok || body?.ok === false) throw v2Error("sql", res, raw2);
66876
- return {
66877
- rows: body?.rows ?? [],
66878
- rowCount: body?.rowCount ?? 0,
66879
- resultSetCount: body?.resultSetCount ?? 1,
66880
- durationMs: body?.durationMs ?? 0
66881
- };
67373
+ var runQuery = async (tableSpaceId, query, params) => {
67374
+ try {
67375
+ return await cliClient().tableSpace(tableSpaceId).sql(query, { params });
67376
+ } catch (error62) {
67377
+ throw withOrgHint(error62);
67378
+ }
66882
67379
  };
66883
67380
  var summarize = (result) => `(${String(result.rowCount)} row${result.rowCount === 1 ? "" : "s"}, ${String(result.durationMs)}ms)`;
66884
67381
  var REPL_HELP = ` \\dt list tables
@@ -66888,7 +67385,7 @@ var REPL_HELP = ` \\dt list tables
66888
67385
  Statements run when a line ends with ";". Each statement runs in its own
66889
67386
  transaction \u2014 BEGIN/SET/temp tables don't carry across statements.
66890
67387
  `;
66891
- var runRepl = async (organizationId, tableSpaceId, format2) => {
67388
+ var runRepl = async (tableSpaceId, format2) => {
66892
67389
  const interactive = process.stdin.isTTY;
66893
67390
  const rl = createInterface3({
66894
67391
  input: process.stdin,
@@ -66904,7 +67401,7 @@ var runRepl = async (organizationId, tableSpaceId, format2) => {
66904
67401
  };
66905
67402
  const execute = async (sql, params) => {
66906
67403
  try {
66907
- const result = await runQuery(organizationId, tableSpaceId, sql, params);
67404
+ const result = await runQuery(tableSpaceId, sql, params);
66908
67405
  if (result.rows.length > 0) {
66909
67406
  process.stdout.write(`${formatResult(result.rows, format2)}
66910
67407
  `);
@@ -67008,7 +67505,7 @@ var runSql = async (args) => {
67008
67505
  );
67009
67506
  }
67010
67507
  const format2 = resolveFormat(flagValue(args, "-o", "--output"));
67011
- const { organizationId } = await getAccessToken();
67508
+ await cliClient().organizationId();
67012
67509
  const query = (() => {
67013
67510
  const rest = args.slice(1);
67014
67511
  for (let i = 0; i < rest.length; i++) {
@@ -67021,9 +67518,8 @@ var runSql = async (args) => {
67021
67518
  }
67022
67519
  return void 0;
67023
67520
  })();
67024
- if (query == null) return runRepl(organizationId, tableSpaceId, format2);
67521
+ if (query == null) return runRepl(tableSpaceId, format2);
67025
67522
  const result = await runQuery(
67026
- organizationId,
67027
67523
  tableSpaceId,
67028
67524
  await readValue(query, "the query") ?? query
67029
67525
  );
@@ -67035,35 +67531,22 @@ var runSql = async (args) => {
67035
67531
  };
67036
67532
 
67037
67533
  // src/commands/storage.ts
67038
- import { openAsBlob, writeFileSync as writeFileSync5 } from "fs";
67534
+ import { openAsBlob as openAsBlob3, writeFileSync as writeFileSync5 } from "fs";
67039
67535
  var USAGE4 = "Usage: overai storage upload <path> [--no-random-prefix] | storage download <key> [path|-] | storage exists <key> | storage head <key> [--lines N] | storage presign <key> [--expires-in <seconds>]";
67040
67536
  var runStorage = async (args) => {
67041
67537
  const [operation, first2, second] = args;
67042
67538
  if (operation == null) throw new Error(USAGE4);
67043
- const { organizationId } = await getAccessToken();
67044
- const segment = (key) => encodeURIComponent(key);
67539
+ const storage = cliClient().storage;
67045
67540
  if (operation === "upload") {
67046
67541
  if (first2 == null || first2 === "-") {
67047
- throw new Error("Upload requires a file path (stdin uploads need a filename to build the key).");
67048
- }
67049
- const randomPrefix = !args.includes("--no-random-prefix");
67050
- const query = randomPrefix ? "" : "?disableRandomPrefix=true";
67051
- const response = await v2Fetch(
67052
- `/v2/storage/${organizationId}/stream${query}`,
67053
- {
67054
- method: "POST",
67055
- headers: {
67056
- "Content-Type": "application/octet-stream",
67057
- "x-file-name": first2.split("/").pop() ?? first2
67058
- },
67059
- body: await openAsBlob(first2)
67060
- }
67061
- );
67062
- if (!response.ok) {
67063
- throw v2Error("storage upload", response, await response.text());
67542
+ throw new Error(
67543
+ "Upload requires a file path (stdin uploads need a filename to build the key)."
67544
+ );
67064
67545
  }
67065
- const { path } = await response.json();
67066
- const key = path.startsWith(`${organizationId}/`) ? path.slice(organizationId.length + 1) : path;
67546
+ const { key, path } = await storage.upload(await openAsBlob3(first2), {
67547
+ name: first2.split("/").pop() ?? first2,
67548
+ randomPrefix: !args.includes("--no-random-prefix")
67549
+ });
67067
67550
  const format2 = resolveFormat(flagValue(args, "-o", "--output"));
67068
67551
  if (format2 === "json") printResult({ key, path }, format2);
67069
67552
  else process.stdout.write(`${key}
@@ -67072,59 +67555,38 @@ var runStorage = async (args) => {
67072
67555
  }
67073
67556
  if (first2 == null) throw new Error(USAGE4);
67074
67557
  if (operation === "download") {
67075
- const response = await v2Fetch(
67076
- `/v2/storage/${organizationId}/${segment(first2)}`,
67077
- { redirect: "follow" }
67078
- );
67079
- if (!response.ok) {
67080
- throw v2Error("storage download", response, await response.text());
67081
- }
67558
+ const response = await storage.download(first2);
67082
67559
  const bytes = Buffer.from(await response.arrayBuffer());
67083
67560
  if (second != null && second !== "-") writeFileSync5(second, bytes);
67084
67561
  else process.stdout.write(bytes);
67085
67562
  return;
67086
67563
  }
67087
67564
  if (operation === "exists") {
67088
- const response = await v2Fetch(
67089
- `/v2/storage/${organizationId}/${segment(first2)}/exists`
67090
- );
67091
- if (response.status === 404) {
67565
+ const meta3 = await storage.exists(first2);
67566
+ if (meta3 == null) {
67092
67567
  if (resolveFormat(flagValue(args, "-o", "--output")) === "json") {
67093
67568
  printResult({ exists: false }, "json");
67094
67569
  }
67095
67570
  process.exitCode = 1;
67096
67571
  return;
67097
67572
  }
67098
- if (!response.ok) {
67099
- throw v2Error("storage exists", response, await response.text());
67100
- }
67101
67573
  printResult(
67102
- { exists: true, ...await response.json() },
67574
+ { exists: true, ...meta3 },
67103
67575
  resolveFormat(flagValue(args, "-o", "--output"))
67104
67576
  );
67105
67577
  return;
67106
67578
  }
67107
67579
  if (operation === "head") {
67108
- const lines = flagValue(args, "--lines") ?? "50";
67109
- const response = await v2Fetch(
67110
- `/v2/storage/${organizationId}/${segment(first2)}/head?numLines=${encodeURIComponent(lines)}`
67580
+ process.stdout.write(
67581
+ await storage.head(first2, { lines: integerFlag(args, "--lines", 50) })
67111
67582
  );
67112
- if (!response.ok) {
67113
- throw v2Error("storage head", response, await response.text());
67114
- }
67115
- process.stdout.write(await response.text());
67116
67583
  return;
67117
67584
  }
67118
67585
  if (operation === "presign") {
67119
- const expiresIn2 = flagValue(args, "--expires-in") ?? String(60 * 60 * 24 * 7);
67120
- const response = await v2Fetch(
67121
- `/v2/storage/${organizationId}/${segment(first2)}/presign?expiresIn=${encodeURIComponent(expiresIn2)}`
67586
+ process.stdout.write(
67587
+ `${await storage.presign(first2, { expiresIn: integerFlag(args, "--expires-in", 60 * 60 * 24 * 7) })}
67588
+ `
67122
67589
  );
67123
- if (!response.ok) {
67124
- throw v2Error("storage presign", response, await response.text());
67125
- }
67126
- process.stdout.write(`${(await response.text()).trim()}
67127
- `);
67128
67590
  return;
67129
67591
  }
67130
67592
  throw new Error(USAGE4);
@@ -67136,7 +67598,7 @@ var workspaceIdFrom = (args) => args.find(
67136
67598
  ) ?? openClone().manifest.workspaceId;
67137
67599
  var listWorkspaces = async (args) => {
67138
67600
  const archived = args.includes("--archived");
67139
- const limit2 = Number(flagValue(args, "--limit") ?? 200);
67601
+ const limit2 = integerFlag(args, "--limit", 200);
67140
67602
  printResult(
67141
67603
  (await cliClient().workspaces.list({
67142
67604
  first: limit2,
@@ -67312,7 +67774,8 @@ Usage:
67312
67774
  overai sql credential rotate|revoke <credentialId>
67313
67775
 
67314
67776
  Workspace sync (edit resources as local files):
67315
- overai clone <workspaceId> [dir] Clone workspace and accessible descendants
67777
+ overai clone <workspaceId> [dir] Clone workspace and accessible descendants;
67778
+ .overai/ holds sync state, ignored by git
67316
67779
  overai status [--recursive] Local edits + remote drift
67317
67780
  overai diff [filter] [--from <ref>] [--to <ref>] [--recursive]
67318
67781
  Diff versions (refs: N, base, local, deployed, latest;