openwork-server 0.18.60 → 0.18.62

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 (81) hide show
  1. package/README.md +9 -2
  2. package/bin/openwork-server.mjs +1 -1
  3. package/bin/platform.mjs +7 -7
  4. package/dist/{opencode-plugin-deps-1.18.30.tgz → opencode-plugin-deps-1.18.33.tgz} +0 -0
  5. package/dist/opencode-plugins/drive-file-tools.js +10 -8
  6. package/dist/opencode-plugins/openwork-cloud-skills-v2.js +87 -41
  7. package/dist/opencode-plugins/openwork-context-v2.js +67 -35
  8. package/dist/opencode-plugins/openwork-extensions-preview.js +9 -7
  9. package/dist/opencode-plugins/openwork-gateway-quota-v2.js +187 -0
  10. package/dist/opencode-plugins/openwork-gateway-quota.js +232 -23
  11. package/dist/opencode-plugins/openwork-provider-sync-v2.js +85 -2
  12. package/dist/openwork-server.mjs +1234 -371
  13. package/package.json +2 -2
  14. package/web/assets/{abnfDiagram-VCTEODGH-D0_4pXEx.js → abnfDiagram-VCTEODGH-DJzrT8Ql.js} +1 -1
  15. package/web/assets/app-CkmqBm_1.css +32 -0
  16. package/web/assets/{app-BCXQnJUH.js → app-_2KkkZIM.js} +435 -425
  17. package/web/assets/{arc-BSTwaImw.js → arc-KehB1xN5.js} +1 -1
  18. package/web/assets/{architectureDiagram-5GKGNRK7-cXwF0iP0.js → architectureDiagram-5GKGNRK7-C_GL2639.js} +1 -1
  19. package/web/assets/{artifact-code-view-CUXv3CYK.js → artifact-code-view-CYn_58gV.js} +1 -1
  20. package/web/assets/{artifact-spreadsheet-editor-CxhNYmjP.js → artifact-spreadsheet-editor-f6V-tYvi.js} +1 -1
  21. package/web/assets/{artifact-text-editor-k2TGQI-i.js → artifact-text-editor-CiSlW3kl.js} +1 -1
  22. package/web/assets/{blockDiagram-OSKFZWR5-Cl9p_z8u.js → blockDiagram-OSKFZWR5-DErH-7gY.js} +1 -1
  23. package/web/assets/{c4Diagram-7LVT6UL2-YQjnoUQ0.js → c4Diagram-7LVT6UL2-aML4z_n9.js} +1 -1
  24. package/web/assets/channel-7oM0ncHI.js +1 -0
  25. package/web/assets/{chunk-2Q5K7J3B-YZaSwlzj.js → chunk-2Q5K7J3B-Dw_IH9rW.js} +1 -1
  26. package/web/assets/{chunk-5VM5RSS4-BICC_wIw.js → chunk-5VM5RSS4-aeJEQ945.js} +1 -1
  27. package/web/assets/{chunk-F27PBJKO-CBxmcmxs.js → chunk-F27PBJKO-hZwti-Oc.js} +1 -1
  28. package/web/assets/{chunk-HLEWEB6X-2h2z7zt8.js → chunk-HLEWEB6X-JFNzm3Ge.js} +1 -1
  29. package/web/assets/{chunk-JWPE2WC7-BnAMOpXl.js → chunk-JWPE2WC7-B34hvYk7.js} +1 -1
  30. package/web/assets/{chunk-POPQ4Y6H-pvDYQBBM.js → chunk-POPQ4Y6H-CDqgEp-q.js} +1 -1
  31. package/web/assets/{chunk-SVP7TREG-QuKLl3v0.js → chunk-SVP7TREG-B65ta9aQ.js} +1 -1
  32. package/web/assets/{chunk-XXDRQBXY-4Bt9wCBO.js → chunk-XXDRQBXY-DjTi6c9m.js} +1 -1
  33. package/web/assets/{chunk-ZLD2IHE6-C_RSrkqU.js → chunk-ZLD2IHE6-CbviXFvb.js} +1 -1
  34. package/web/assets/classDiagram-CYGNFDIV-BkidFauP.js +1 -0
  35. package/web/assets/classDiagram-v2-TLXNO2FR-BkidFauP.js +1 -0
  36. package/web/assets/{cose-bilkent-JH36ORCC-CciOYQb4.js → cose-bilkent-JH36ORCC-Cl9SO5zd.js} +1 -1
  37. package/web/assets/{cynefin-OW5HDTMX-k8-mUlq4.js → cynefin-OW5HDTMX-CAcz2tHt.js} +1 -1
  38. package/web/assets/{cynefinDiagram-5FMLGOSQ-D1qAHzq3.js → cynefinDiagram-5FMLGOSQ-BJHft4FO.js} +1 -1
  39. package/web/assets/{dagre-OS7QT2EB-BP1VuF_z.js → dagre-OS7QT2EB-DbdnY1uS.js} +1 -1
  40. package/web/assets/{diagram-S7CK7UJ4-crXkWleC.js → diagram-S7CK7UJ4-C07WD5Lc.js} +1 -1
  41. package/web/assets/{diagram-UQ7AKVKN-CtHAmCY-.js → diagram-UQ7AKVKN-D8VCjq9x.js} +1 -1
  42. package/web/assets/{diagram-VSXAHHWV-C-Fhuwy8.js → diagram-VSXAHHWV-D1XSfMnK.js} +1 -1
  43. package/web/assets/{diagram-VX7I27RA-DkRkNPuj.js → diagram-VX7I27RA-3GZe-yrf.js} +1 -1
  44. package/web/assets/{diagram-Z3DM3KII-DVERPCFS.js → diagram-Z3DM3KII-CI43WcIB.js} +1 -1
  45. package/web/assets/{ebnfDiagram-PWID7BFC-PrSifYGF.js → ebnfDiagram-PWID7BFC-CTzouade.js} +1 -1
  46. package/web/assets/{erDiagram-2YWLMYGG-D8-wfUYq.js → erDiagram-2YWLMYGG-CNcqsnqx.js} +1 -1
  47. package/web/assets/{flowDiagram-T62WH6J4-BS-_wkGe.js → flowDiagram-T62WH6J4-pomAqjgF.js} +1 -1
  48. package/web/assets/{ganttDiagram-EL5Y4UJY-D9PWmuWE.js → ganttDiagram-EL5Y4UJY-C1dNaxxj.js} +1 -1
  49. package/web/assets/{gitGraphDiagram-WWUBYQGX-DIP9p63c.js → gitGraphDiagram-WWUBYQGX-Ni8GECg1.js} +1 -1
  50. package/web/assets/{infoDiagram-FKFFQAWI-BzU6-acC.js → infoDiagram-FKFFQAWI-C0kvE7Zu.js} +1 -1
  51. package/web/assets/{ishikawaDiagram-5VMMS53U-CvYS2ayc.js → ishikawaDiagram-5VMMS53U-CxmP_irp.js} +1 -1
  52. package/web/assets/{journeyDiagram-EYS64GPL-Cr0qdK-D.js → journeyDiagram-EYS64GPL-60GkPgH5.js} +1 -1
  53. package/web/assets/{kanban-definition-UXKFOSKX-DhffGp7U.js → kanban-definition-UXKFOSKX-ByxcB0T_.js} +1 -1
  54. package/web/assets/{layout-b5jqOIam.js → layout-BD9ef90s.js} +1 -1
  55. package/web/assets/{linear-zLNMwGhI.js → linear-Dp9l0lBr.js} +1 -1
  56. package/web/assets/{mermaid.core-DB_TNiSG.js → mermaid.core-FKm3Akes.js} +6 -6
  57. package/web/assets/{mindmap-definition-THT77NOG-DkQz2hbh.js → mindmap-definition-THT77NOG-i7WJlWHV.js} +1 -1
  58. package/web/assets/{pegDiagram-XKGWAZYB-CB313CDF.js → pegDiagram-XKGWAZYB-DfI-LsJq.js} +1 -1
  59. package/web/assets/{pieDiagram-E7YTZNPT-yaDJVY4d.js → pieDiagram-E7YTZNPT-kcgWYBSb.js} +1 -1
  60. package/web/assets/{quadrantDiagram-AXDQQJYC-BkV8iwHS.js → quadrantDiagram-AXDQQJYC-DVyziZnc.js} +1 -1
  61. package/web/assets/{railroadDiagram-O6MQD6OU-T_JVDRpV.js → railroadDiagram-O6MQD6OU-CWgHq9nv.js} +1 -1
  62. package/web/assets/{requirementDiagram-IS5BZ75X-B68yTCqr.js → requirementDiagram-IS5BZ75X-B7fgi4lz.js} +1 -1
  63. package/web/assets/{sankeyDiagram-P5KCCOFB-Bx31O26O.js → sankeyDiagram-P5KCCOFB-D1-pD_qh.js} +1 -1
  64. package/web/assets/{sequenceDiagram-WJ2MYXX4-CsCoFTAg.js → sequenceDiagram-WJ2MYXX4-B6oeRmVj.js} +1 -1
  65. package/web/assets/{sizeCapture-INFHLROL-B3a49hkJ.js → sizeCapture-INFHLROL-BTpKGFt5.js} +1 -1
  66. package/web/assets/{stateDiagram-XQSTLZYL-DjCdHmx5.js → stateDiagram-XQSTLZYL-zXJRxE6h.js} +1 -1
  67. package/web/assets/stateDiagram-v2-IH3M54BS-CmwZ6tXj.js +1 -0
  68. package/web/assets/{swimlanes-V6O3JKXN-4u8S1I3e.js → swimlanes-V6O3JKXN-Dc7MXPb3.js} +1 -1
  69. package/web/assets/swimlanesDiagram-JKAHXJPX-BXoJEx2b.js +8 -0
  70. package/web/assets/{timeline-definition-24CTP7MA-r3GnOjJo.js → timeline-definition-24CTP7MA-DYhgbnjW.js} +1 -1
  71. package/web/assets/{vennDiagram-4TSXK5OY-59IlTU54.js → vennDiagram-4TSXK5OY-DRnme7za.js} +1 -1
  72. package/web/assets/{wardleyDiagram-VM6X3IG4-C3ALfLHE.js → wardleyDiagram-VM6X3IG4-wAoo4GsZ.js} +1 -1
  73. package/web/assets/{workspace-file-tree-C6GhNx3L.js → workspace-file-tree-DmAwvqtx.js} +1 -1
  74. package/web/assets/{xychartDiagram-S5SC5T6Z-CoYtWlkb.js → xychartDiagram-S5SC5T6Z-_Z_TOWBn.js} +1 -1
  75. package/web/index.html +2 -2
  76. package/web/assets/app-DVblmMbF.css +0 -32
  77. package/web/assets/channel-DwV664-P.js +0 -1
  78. package/web/assets/classDiagram-CYGNFDIV-BKhd9faI.js +0 -1
  79. package/web/assets/classDiagram-v2-TLXNO2FR-BKhd9faI.js +0 -1
  80. package/web/assets/stateDiagram-v2-IH3M54BS-Cf0tqY7x.js +0 -1
  81. package/web/assets/swimlanesDiagram-JKAHXJPX-COK5qx0u.js +0 -8
@@ -13739,6 +13739,138 @@ function hasGatewayUsageLimitHttpMarker(response) {
13739
13739
  return response.status === 429 && response.headers.get("X-OpenWork-Error-Code") === GATEWAY_USAGE_LIMIT_ERROR_CODE && response.headers.get("X-OpenWork-Usage-State") === "blocked";
13740
13740
  }
13741
13741
 
13742
+ // ../../packages/types/src/den/inference.ts
13743
+ var INFERENCE_WINDOW_DURATIONS_MS = {
13744
+ five_hour: 5 * 60 * 60 * 1000,
13745
+ weekly: 7 * 24 * 60 * 60 * 1000,
13746
+ monthly: 30 * 24 * 60 * 60 * 1000
13747
+ };
13748
+ var OPENWORK_MODELS_ALLOWANCE_ERROR_CODE = "openwork_models_allowance_reached";
13749
+ var OPENWORK_MODELS_ALLOWANCE_SOURCE = "openwork_models";
13750
+ var OPENWORK_MODELS_ALLOWANCE_MESSAGE = "Your workspace used its OpenWork Models allowance.";
13751
+ function openWorkModelsAllowanceErrorBody() {
13752
+ return {
13753
+ error: {
13754
+ message: OPENWORK_MODELS_ALLOWANCE_MESSAGE,
13755
+ type: "usage_limit_error",
13756
+ param: null,
13757
+ code: OPENWORK_MODELS_ALLOWANCE_ERROR_CODE,
13758
+ source: OPENWORK_MODELS_ALLOWANCE_SOURCE
13759
+ }
13760
+ };
13761
+ }
13762
+ function hasOpenWorkModelsAllowanceHttpMarker(response) {
13763
+ return response.status === 429 && response.headers.get("X-OpenWork-Error-Code") === OPENWORK_MODELS_ALLOWANCE_ERROR_CODE && response.headers.get("X-OpenWork-Usage-State") === "blocked";
13764
+ }
13765
+ var INFERENCE_ACCESS_REASONS = [
13766
+ "admin_disabled",
13767
+ "not_eligible",
13768
+ "free_disabled",
13769
+ "accounting_unavailable",
13770
+ "free_allowance_exhausted",
13771
+ "upstream_unavailable"
13772
+ ];
13773
+ var freeInferenceProviderSummarySchema = exports_external.object({
13774
+ state: exports_external.enum(["available", "disabled", "unavailable"]),
13775
+ reason: exports_external.union([exports_external.enum(INFERENCE_ACCESS_REASONS), exports_external.null()]),
13776
+ defaultPinned: exports_external.boolean(),
13777
+ modelGroup: exports_external.object({ id: exports_external.literal("free"), name: exports_external.literal("Free") }),
13778
+ catalog: exports_external.array(exports_external.object({ modelID: exports_external.string(), displayName: exports_external.string(), providerName: exports_external.string(), summary: exports_external.string(), recommended: exports_external.boolean(), rank: exports_external.number(), capabilities: exports_external.array(exports_external.string()) })),
13779
+ allowance: exports_external.object({
13780
+ usageScope: exports_external.literal("organization"),
13781
+ allowanceScope: exports_external.literal("person"),
13782
+ windowStartAt: exports_external.string().datetime(),
13783
+ resetsAt: exports_external.string().datetime(),
13784
+ weeklyLimitUsd: exports_external.number().finite().nonnegative(),
13785
+ joinedMembers: exports_external.number().int().nonnegative(),
13786
+ eligibleMembers: exports_external.number().int().nonnegative(),
13787
+ exhaustedMembers: exports_external.number().int().nonnegative().nullable().describe("Current eligible members whose recorded weekly usage has reached their person-wide limit; not a probe of Gateway request headroom. Null when accounting cannot be verified."),
13788
+ usedUsd: exports_external.number().finite().nonnegative().nullable(),
13789
+ requestCount: exports_external.number().int().nonnegative().nullable()
13790
+ })
13791
+ });
13792
+ var inferenceApiKeyMapSecretSchema = exports_external.record(exports_external.string(), exports_external.string()).refine((value) => Object.keys(value).length > 0, {
13793
+ message: "api_key_map must contain at least one entry"
13794
+ });
13795
+ var inferenceAwsKeysSecretSchema = exports_external.object({
13796
+ accessKeyId: exports_external.string().min(1),
13797
+ secretAccessKey: exports_external.string().min(1),
13798
+ sessionToken: exports_external.string().min(1).optional(),
13799
+ region: exports_external.string().min(1).optional()
13800
+ });
13801
+ var inferenceGcpServiceAccountSecretSchema = exports_external.looseObject({
13802
+ client_email: exports_external.string().min(1),
13803
+ private_key: exports_external.string().min(1),
13804
+ token_uri: exports_external.string().min(1)
13805
+ });
13806
+ var authorizationRevisionSchema = exports_external.string().regex(/^[A-Za-z0-9_-]{43}$/);
13807
+ var microsoftTenantIdSchema = exports_external.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
13808
+ var inferenceOauthTokenSecretSchema = exports_external.object({
13809
+ accessToken: exports_external.string().min(1),
13810
+ refreshToken: exports_external.string().min(1).optional(),
13811
+ tokenType: exports_external.string().min(1).optional(),
13812
+ googleIdentity: exports_external.object({
13813
+ subject: exports_external.string().min(1).max(255),
13814
+ email: exports_external.email(),
13815
+ emailVerified: exports_external.literal(true),
13816
+ clientId: exports_external.string().min(1),
13817
+ authorizationRevision: authorizationRevisionSchema.optional()
13818
+ }).optional(),
13819
+ microsoftIdentity: exports_external.object({
13820
+ tenantId: microsoftTenantIdSchema,
13821
+ objectId: exports_external.string().min(1).max(255),
13822
+ userName: exports_external.string().min(1).max(320).nullable(),
13823
+ clientId: exports_external.string().min(1),
13824
+ authorizationRevision: authorizationRevisionSchema
13825
+ }).optional()
13826
+ });
13827
+ var awsAccountIdSchema = exports_external.string().regex(/^\d{12}$/);
13828
+ var awsPermissionSetNameSchema = exports_external.string().regex(/^[\w+=,.@-]{1,32}$/);
13829
+ var awsSsoStartUrlSchema = exports_external.string().max(2048).refine((value) => {
13830
+ try {
13831
+ const url2 = new URL(value);
13832
+ return url2.protocol === "https:" && !url2.username && !url2.password && !url2.search && !url2.hash && !url2.port && /^[a-z0-9-]+(?:\.[a-z0-9-]+)*\.(?:awsapps\.com|app\.aws)$/i.test(url2.hostname);
13833
+ } catch {
13834
+ return false;
13835
+ }
13836
+ }, { message: "Use the AWS access portal URL, for example https://d-xxxxxxxxxx.awsapps.com/start." });
13837
+ var awsRegionSchema = exports_external.string().max(32).regex(/^[a-z]{2}(?:-[a-z]+)+-\d{1,2}$/);
13838
+ var gatewayAwsSsoSettingsSchema = exports_external.object({
13839
+ startUrl: awsSsoStartUrlSchema,
13840
+ region: awsRegionSchema,
13841
+ accountId: awsAccountIdSchema,
13842
+ roleName: awsPermissionSetNameSchema
13843
+ }).strict();
13844
+ var inferenceAwsSsoSecretSchema = exports_external.object({
13845
+ accessToken: exports_external.string().min(1).max(16384),
13846
+ refreshToken: exports_external.string().min(1).max(16384),
13847
+ clientId: exports_external.string().min(1).max(4096),
13848
+ clientSecret: exports_external.string().min(1).max(16384),
13849
+ clientSecretExpiresAt: exports_external.number().int().positive(),
13850
+ sso: gatewayAwsSsoSettingsSchema,
13851
+ identity: exports_external.object({
13852
+ arn: exports_external.string().min(1).max(2048),
13853
+ userName: exports_external.string().min(1).max(320).nullable(),
13854
+ authorizationRevision: authorizationRevisionSchema
13855
+ })
13856
+ });
13857
+ var GATEWAY_MEMBER_SIGN_IN_METHODS = ["google", "aws_sso", "microsoft"];
13858
+ var gatewayMemberConnectionsResponseSchema = exports_external.object({
13859
+ connections: exports_external.array(exports_external.object({
13860
+ providerId: exports_external.string(),
13861
+ signInMethod: exports_external.enum(GATEWAY_MEMBER_SIGN_IN_METHODS).default("google"),
13862
+ credentialSetId: exports_external.string(),
13863
+ providerName: exports_external.string(),
13864
+ name: exports_external.string(),
13865
+ ready: exports_external.boolean(),
13866
+ hasAccess: exports_external.boolean(),
13867
+ hasCredential: exports_external.boolean(),
13868
+ configurationRequired: exports_external.boolean().default(false),
13869
+ authorizationRevision: exports_external.string().nullable(),
13870
+ accountEmail: exports_external.string().nullable()
13871
+ }))
13872
+ });
13873
+
13742
13874
  // src/gateway-quota.ts
13743
13875
  var GATEWAY_QUOTA_MESSAGE = "You have reached your AI Gateway usage limit.";
13744
13876
  var MAX_ERROR_BYTES = 16384;
@@ -13761,6 +13893,61 @@ function gatewayBase(id, baseURL) {
13761
13893
  return;
13762
13894
  }
13763
13895
  }
13896
+ function openWorkModelsBase(id, baseURL) {
13897
+ if (id !== "openwork" || typeof baseURL !== "string")
13898
+ return;
13899
+ try {
13900
+ const url2 = new URL(baseURL);
13901
+ if (!["https:", "http:"].includes(url2.protocol) || url2.username || url2.password || url2.search || url2.hash)
13902
+ return;
13903
+ url2.pathname = url2.pathname.replace(/\/+$/, "");
13904
+ if (!url2.pathname.endsWith("/api/v1"))
13905
+ return;
13906
+ return url2;
13907
+ } catch {
13908
+ return;
13909
+ }
13910
+ }
13911
+ async function isOpenWorkModelsAllowanceResponse(base, url2, response) {
13912
+ if (url2.origin !== base.origin || !url2.pathname.startsWith(`${base.pathname}/`) || response.url && new URL(response.url).origin !== base.origin || !hasOpenWorkModelsAllowanceHttpMarker(response))
13913
+ return false;
13914
+ const body = await readBoundedJson(response);
13915
+ return record2(body) && record2(body.error) && body.error.code === OPENWORK_MODELS_ALLOWANCE_ERROR_CODE && body.error.source === OPENWORK_MODELS_ALLOWANCE_SOURCE;
13916
+ }
13917
+ async function readBoundedJson(response) {
13918
+ const reader = response.clone().body?.getReader();
13919
+ if (!reader)
13920
+ return null;
13921
+ let timer;
13922
+ const expired = new Promise((resolve) => {
13923
+ timer = setTimeout(() => resolve(null), BODY_TIMEOUT_MS);
13924
+ });
13925
+ const read = async () => {
13926
+ let size = 0;
13927
+ let text = "";
13928
+ const decoder = new TextDecoder;
13929
+ while (true) {
13930
+ const chunk = await reader.read();
13931
+ if (chunk.done)
13932
+ break;
13933
+ size += chunk.value.byteLength;
13934
+ if (size > MAX_ERROR_BYTES)
13935
+ return null;
13936
+ text += decoder.decode(chunk.value, { stream: true });
13937
+ }
13938
+ return JSON.parse(text + decoder.decode());
13939
+ };
13940
+ try {
13941
+ return await Promise.race([read(), expired]);
13942
+ } catch {
13943
+ return null;
13944
+ } finally {
13945
+ clearTimeout(timer);
13946
+ reader.cancel().catch(() => {
13947
+ return;
13948
+ });
13949
+ }
13950
+ }
13764
13951
  async function isGatewayQuotaResponse(base, url2, response) {
13765
13952
  if (url2.origin !== base.origin || !url2.pathname.startsWith(`${base.pathname}/`) || response.url && new URL(response.url).origin !== base.origin || !hasGatewayUsageLimitHttpMarker(response))
13766
13953
  return false;
@@ -14053,6 +14053,138 @@ function hasGatewayUsageLimitHttpMarker(response) {
14053
14053
  return response.status === 429 && response.headers.get("X-OpenWork-Error-Code") === GATEWAY_USAGE_LIMIT_ERROR_CODE && response.headers.get("X-OpenWork-Usage-State") === "blocked";
14054
14054
  }
14055
14055
 
14056
+ // ../../packages/types/src/den/inference.ts
14057
+ var INFERENCE_WINDOW_DURATIONS_MS = {
14058
+ five_hour: 5 * 60 * 60 * 1000,
14059
+ weekly: 7 * 24 * 60 * 60 * 1000,
14060
+ monthly: 30 * 24 * 60 * 60 * 1000
14061
+ };
14062
+ var OPENWORK_MODELS_ALLOWANCE_ERROR_CODE = "openwork_models_allowance_reached";
14063
+ var OPENWORK_MODELS_ALLOWANCE_SOURCE = "openwork_models";
14064
+ var OPENWORK_MODELS_ALLOWANCE_MESSAGE = "Your workspace used its OpenWork Models allowance.";
14065
+ function openWorkModelsAllowanceErrorBody() {
14066
+ return {
14067
+ error: {
14068
+ message: OPENWORK_MODELS_ALLOWANCE_MESSAGE,
14069
+ type: "usage_limit_error",
14070
+ param: null,
14071
+ code: OPENWORK_MODELS_ALLOWANCE_ERROR_CODE,
14072
+ source: OPENWORK_MODELS_ALLOWANCE_SOURCE
14073
+ }
14074
+ };
14075
+ }
14076
+ function hasOpenWorkModelsAllowanceHttpMarker(response) {
14077
+ return response.status === 429 && response.headers.get("X-OpenWork-Error-Code") === OPENWORK_MODELS_ALLOWANCE_ERROR_CODE && response.headers.get("X-OpenWork-Usage-State") === "blocked";
14078
+ }
14079
+ var INFERENCE_ACCESS_REASONS = [
14080
+ "admin_disabled",
14081
+ "not_eligible",
14082
+ "free_disabled",
14083
+ "accounting_unavailable",
14084
+ "free_allowance_exhausted",
14085
+ "upstream_unavailable"
14086
+ ];
14087
+ var freeInferenceProviderSummarySchema = exports_external.object({
14088
+ state: exports_external.enum(["available", "disabled", "unavailable"]),
14089
+ reason: exports_external.union([exports_external.enum(INFERENCE_ACCESS_REASONS), exports_external.null()]),
14090
+ defaultPinned: exports_external.boolean(),
14091
+ modelGroup: exports_external.object({ id: exports_external.literal("free"), name: exports_external.literal("Free") }),
14092
+ catalog: exports_external.array(exports_external.object({ modelID: exports_external.string(), displayName: exports_external.string(), providerName: exports_external.string(), summary: exports_external.string(), recommended: exports_external.boolean(), rank: exports_external.number(), capabilities: exports_external.array(exports_external.string()) })),
14093
+ allowance: exports_external.object({
14094
+ usageScope: exports_external.literal("organization"),
14095
+ allowanceScope: exports_external.literal("person"),
14096
+ windowStartAt: exports_external.string().datetime(),
14097
+ resetsAt: exports_external.string().datetime(),
14098
+ weeklyLimitUsd: exports_external.number().finite().nonnegative(),
14099
+ joinedMembers: exports_external.number().int().nonnegative(),
14100
+ eligibleMembers: exports_external.number().int().nonnegative(),
14101
+ exhaustedMembers: exports_external.number().int().nonnegative().nullable().describe("Current eligible members whose recorded weekly usage has reached their person-wide limit; not a probe of Gateway request headroom. Null when accounting cannot be verified."),
14102
+ usedUsd: exports_external.number().finite().nonnegative().nullable(),
14103
+ requestCount: exports_external.number().int().nonnegative().nullable()
14104
+ })
14105
+ });
14106
+ var inferenceApiKeyMapSecretSchema = exports_external.record(exports_external.string(), exports_external.string()).refine((value) => Object.keys(value).length > 0, {
14107
+ message: "api_key_map must contain at least one entry"
14108
+ });
14109
+ var inferenceAwsKeysSecretSchema = exports_external.object({
14110
+ accessKeyId: exports_external.string().min(1),
14111
+ secretAccessKey: exports_external.string().min(1),
14112
+ sessionToken: exports_external.string().min(1).optional(),
14113
+ region: exports_external.string().min(1).optional()
14114
+ });
14115
+ var inferenceGcpServiceAccountSecretSchema = exports_external.looseObject({
14116
+ client_email: exports_external.string().min(1),
14117
+ private_key: exports_external.string().min(1),
14118
+ token_uri: exports_external.string().min(1)
14119
+ });
14120
+ var authorizationRevisionSchema = exports_external.string().regex(/^[A-Za-z0-9_-]{43}$/);
14121
+ var microsoftTenantIdSchema = exports_external.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
14122
+ var inferenceOauthTokenSecretSchema = exports_external.object({
14123
+ accessToken: exports_external.string().min(1),
14124
+ refreshToken: exports_external.string().min(1).optional(),
14125
+ tokenType: exports_external.string().min(1).optional(),
14126
+ googleIdentity: exports_external.object({
14127
+ subject: exports_external.string().min(1).max(255),
14128
+ email: exports_external.email(),
14129
+ emailVerified: exports_external.literal(true),
14130
+ clientId: exports_external.string().min(1),
14131
+ authorizationRevision: authorizationRevisionSchema.optional()
14132
+ }).optional(),
14133
+ microsoftIdentity: exports_external.object({
14134
+ tenantId: microsoftTenantIdSchema,
14135
+ objectId: exports_external.string().min(1).max(255),
14136
+ userName: exports_external.string().min(1).max(320).nullable(),
14137
+ clientId: exports_external.string().min(1),
14138
+ authorizationRevision: authorizationRevisionSchema
14139
+ }).optional()
14140
+ });
14141
+ var awsAccountIdSchema = exports_external.string().regex(/^\d{12}$/);
14142
+ var awsPermissionSetNameSchema = exports_external.string().regex(/^[\w+=,.@-]{1,32}$/);
14143
+ var awsSsoStartUrlSchema = exports_external.string().max(2048).refine((value) => {
14144
+ try {
14145
+ const url2 = new URL(value);
14146
+ return url2.protocol === "https:" && !url2.username && !url2.password && !url2.search && !url2.hash && !url2.port && /^[a-z0-9-]+(?:\.[a-z0-9-]+)*\.(?:awsapps\.com|app\.aws)$/i.test(url2.hostname);
14147
+ } catch {
14148
+ return false;
14149
+ }
14150
+ }, { message: "Use the AWS access portal URL, for example https://d-xxxxxxxxxx.awsapps.com/start." });
14151
+ var awsRegionSchema = exports_external.string().max(32).regex(/^[a-z]{2}(?:-[a-z]+)+-\d{1,2}$/);
14152
+ var gatewayAwsSsoSettingsSchema = exports_external.object({
14153
+ startUrl: awsSsoStartUrlSchema,
14154
+ region: awsRegionSchema,
14155
+ accountId: awsAccountIdSchema,
14156
+ roleName: awsPermissionSetNameSchema
14157
+ }).strict();
14158
+ var inferenceAwsSsoSecretSchema = exports_external.object({
14159
+ accessToken: exports_external.string().min(1).max(16384),
14160
+ refreshToken: exports_external.string().min(1).max(16384),
14161
+ clientId: exports_external.string().min(1).max(4096),
14162
+ clientSecret: exports_external.string().min(1).max(16384),
14163
+ clientSecretExpiresAt: exports_external.number().int().positive(),
14164
+ sso: gatewayAwsSsoSettingsSchema,
14165
+ identity: exports_external.object({
14166
+ arn: exports_external.string().min(1).max(2048),
14167
+ userName: exports_external.string().min(1).max(320).nullable(),
14168
+ authorizationRevision: authorizationRevisionSchema
14169
+ })
14170
+ });
14171
+ var GATEWAY_MEMBER_SIGN_IN_METHODS = ["google", "aws_sso", "microsoft"];
14172
+ var gatewayMemberConnectionsResponseSchema = exports_external.object({
14173
+ connections: exports_external.array(exports_external.object({
14174
+ providerId: exports_external.string(),
14175
+ signInMethod: exports_external.enum(GATEWAY_MEMBER_SIGN_IN_METHODS).default("google"),
14176
+ credentialSetId: exports_external.string(),
14177
+ providerName: exports_external.string(),
14178
+ name: exports_external.string(),
14179
+ ready: exports_external.boolean(),
14180
+ hasAccess: exports_external.boolean(),
14181
+ hasCredential: exports_external.boolean(),
14182
+ configurationRequired: exports_external.boolean().default(false),
14183
+ authorizationRevision: exports_external.string().nullable(),
14184
+ accountEmail: exports_external.string().nullable()
14185
+ }))
14186
+ });
14187
+
14056
14188
  // src/gateway-quota.ts
14057
14189
  var GATEWAY_QUOTA_MESSAGE = "You have reached your AI Gateway usage limit.";
14058
14190
  var MAX_ERROR_BYTES = 16384;
@@ -14075,6 +14207,61 @@ function gatewayBase(id, baseURL) {
14075
14207
  return;
14076
14208
  }
14077
14209
  }
14210
+ function openWorkModelsBase(id, baseURL) {
14211
+ if (id !== "openwork" || typeof baseURL !== "string")
14212
+ return;
14213
+ try {
14214
+ const url2 = new URL(baseURL);
14215
+ if (!["https:", "http:"].includes(url2.protocol) || url2.username || url2.password || url2.search || url2.hash)
14216
+ return;
14217
+ url2.pathname = url2.pathname.replace(/\/+$/, "");
14218
+ if (!url2.pathname.endsWith("/api/v1"))
14219
+ return;
14220
+ return url2;
14221
+ } catch {
14222
+ return;
14223
+ }
14224
+ }
14225
+ async function isOpenWorkModelsAllowanceResponse(base, url2, response) {
14226
+ if (url2.origin !== base.origin || !url2.pathname.startsWith(`${base.pathname}/`) || response.url && new URL(response.url).origin !== base.origin || !hasOpenWorkModelsAllowanceHttpMarker(response))
14227
+ return false;
14228
+ const body = await readBoundedJson(response);
14229
+ return record2(body) && record2(body.error) && body.error.code === OPENWORK_MODELS_ALLOWANCE_ERROR_CODE && body.error.source === OPENWORK_MODELS_ALLOWANCE_SOURCE;
14230
+ }
14231
+ async function readBoundedJson(response) {
14232
+ const reader = response.clone().body?.getReader();
14233
+ if (!reader)
14234
+ return null;
14235
+ let timer;
14236
+ const expired = new Promise((resolve) => {
14237
+ timer = setTimeout(() => resolve(null), BODY_TIMEOUT_MS);
14238
+ });
14239
+ const read = async () => {
14240
+ let size = 0;
14241
+ let text = "";
14242
+ const decoder = new TextDecoder;
14243
+ while (true) {
14244
+ const chunk = await reader.read();
14245
+ if (chunk.done)
14246
+ break;
14247
+ size += chunk.value.byteLength;
14248
+ if (size > MAX_ERROR_BYTES)
14249
+ return null;
14250
+ text += decoder.decode(chunk.value, { stream: true });
14251
+ }
14252
+ return JSON.parse(text + decoder.decode());
14253
+ };
14254
+ try {
14255
+ return await Promise.race([read(), expired]);
14256
+ } catch {
14257
+ return null;
14258
+ } finally {
14259
+ clearTimeout(timer);
14260
+ reader.cancel().catch(() => {
14261
+ return;
14262
+ });
14263
+ }
14264
+ }
14078
14265
  async function isGatewayQuotaResponse(base, url2, response) {
14079
14266
  if (url2.origin !== base.origin || !url2.pathname.startsWith(`${base.pathname}/`) || response.url && new URL(response.url).origin !== base.origin || !hasGatewayUsageLimitHttpMarker(response))
14080
14267
  return false;
@@ -14114,38 +14301,60 @@ async function isGatewayQuotaResponse(base, url2, response) {
14114
14301
  }
14115
14302
 
14116
14303
  // src/opencode-plugins/openwork-gateway-quota.ts
14304
+ function noRetryFetch(base, matches, error48) {
14305
+ return async (input, init) => {
14306
+ const response = await globalThis.fetch(input, init);
14307
+ const url2 = new URL(input instanceof Request ? input.url : input.toString());
14308
+ if (!await matches(base, url2, response))
14309
+ return response;
14310
+ response.body?.cancel().catch(() => {
14311
+ return;
14312
+ });
14313
+ throw error48(url2, response);
14314
+ };
14315
+ }
14117
14316
  var OpenWorkGatewayQuota = async () => ({
14118
14317
  config: async (config2) => {
14119
14318
  for (const [id, provider] of Object.entries(config2.provider ?? {})) {
14120
- if (!record2(provider) || !record2(provider.options))
14121
- continue;
14122
- const options = provider.options;
14123
- const base = gatewayBase(id, options.baseURL);
14124
- if (!base || options.fetch !== undefined)
14319
+ if (!record2(provider))
14125
14320
  continue;
14126
- options.fetch = async (input, init) => {
14127
- const response = await globalThis.fetch(input, init);
14128
- const url2 = new URL(input instanceof Request ? input.url : input.toString());
14129
- if (!await isGatewayQuotaResponse(base, url2, response))
14130
- return response;
14131
- response.body?.cancel().catch(() => {
14132
- return;
14133
- });
14134
- throw new APICallError({
14135
- message: GATEWAY_QUOTA_MESSAGE,
14321
+ const models = openWorkModelsBase(id, record2(provider.options) ? provider.options.baseURL ?? provider.api : provider.api);
14322
+ if (models) {
14323
+ const options2 = record2(provider.options) ? provider.options : {};
14324
+ if (options2.fetch !== undefined)
14325
+ continue;
14326
+ options2.fetch = noRetryFetch(models, isOpenWorkModelsAllowanceResponse, (url2, response) => new APICallError({
14327
+ message: OPENWORK_MODELS_ALLOWANCE_MESSAGE,
14136
14328
  url: `${url2.origin}${url2.pathname}`,
14137
14329
  requestBodyValues: undefined,
14138
14330
  statusCode: response.status,
14139
14331
  responseHeaders: Object.fromEntries(response.headers),
14140
- responseBody: JSON.stringify({ error: {
14141
- type: "usage_limit_error",
14142
- code: GATEWAY_USAGE_LIMIT_ERROR_CODE,
14143
- source: "openwork_gateway",
14144
- message: GATEWAY_QUOTA_MESSAGE
14145
- } }),
14332
+ responseBody: JSON.stringify(openWorkModelsAllowanceErrorBody()),
14146
14333
  isRetryable: false
14147
- });
14148
- };
14334
+ }));
14335
+ provider.options = options2;
14336
+ continue;
14337
+ }
14338
+ if (!record2(provider.options))
14339
+ continue;
14340
+ const options = provider.options;
14341
+ const base = gatewayBase(id, options.baseURL);
14342
+ if (!base || options.fetch !== undefined)
14343
+ continue;
14344
+ options.fetch = noRetryFetch(base, isGatewayQuotaResponse, (url2, response) => new APICallError({
14345
+ message: GATEWAY_QUOTA_MESSAGE,
14346
+ url: `${url2.origin}${url2.pathname}`,
14347
+ requestBodyValues: undefined,
14348
+ statusCode: response.status,
14349
+ responseHeaders: Object.fromEntries(response.headers),
14350
+ responseBody: JSON.stringify({ error: {
14351
+ type: "usage_limit_error",
14352
+ code: GATEWAY_USAGE_LIMIT_ERROR_CODE,
14353
+ source: "openwork_gateway",
14354
+ message: GATEWAY_QUOTA_MESSAGE
14355
+ } }),
14356
+ isRetryable: false
14357
+ }));
14149
14358
  }
14150
14359
  }
14151
14360
  });
@@ -2944,6 +2944,87 @@ function toProviderRecord(provider, apiKey) {
2944
2944
  return { info, models };
2945
2945
  }
2946
2946
 
2947
+ // ../../packages/opencode-plugin/src/inventory-fetch.ts
2948
+ function abortError(signal) {
2949
+ return signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
2950
+ }
2951
+ function createInventoryFetchHub() {
2952
+ const shared = new Map;
2953
+ let sequence = 0;
2954
+ const ticket = () => ++sequence;
2955
+ const start = (key, request) => {
2956
+ const controller = new AbortController;
2957
+ const entry = {
2958
+ startedAfter: ++sequence,
2959
+ controller,
2960
+ joiners: 0,
2961
+ settled: false,
2962
+ result: fetchGatewayInventory(request.fetch, request.session, controller.signal, request.scope)
2963
+ };
2964
+ const settle = () => {
2965
+ entry.settled = true;
2966
+ if (shared.get(key) === entry)
2967
+ shared.delete(key);
2968
+ };
2969
+ entry.result.then(settle, settle);
2970
+ shared.set(key, entry);
2971
+ return entry;
2972
+ };
2973
+ const fetch2 = async (request) => {
2974
+ if (request.signal?.aborted)
2975
+ throw abortError(request.signal);
2976
+ const key = `${inventorySessionKey(request.session)}:${request.scope}`;
2977
+ const entry = shared.get(key);
2978
+ const current = entry && !entry.settled && entry.startedAfter > request.ticket ? entry : start(key, request);
2979
+ current.joiners += 1;
2980
+ const leave = () => {
2981
+ current.joiners -= 1;
2982
+ if (current.joiners === 0 && !current.settled)
2983
+ current.controller.abort();
2984
+ };
2985
+ const signal = request.signal;
2986
+ if (!signal) {
2987
+ try {
2988
+ return await current.result;
2989
+ } finally {
2990
+ leave();
2991
+ }
2992
+ }
2993
+ let onAbort;
2994
+ const aborted = new Promise((_, reject) => {
2995
+ onAbort = () => reject(abortError(signal));
2996
+ signal.addEventListener("abort", onAbort, { once: true });
2997
+ });
2998
+ try {
2999
+ return await Promise.race([current.result, aborted]);
3000
+ } finally {
3001
+ if (onAbort)
3002
+ signal.removeEventListener("abort", onAbort);
3003
+ leave();
3004
+ }
3005
+ };
3006
+ return Object.freeze({ ticket: Object.freeze(ticket), fetch: Object.freeze(fetch2) });
3007
+ }
3008
+ function hub() {
3009
+ let current = globalThis.__openworkGatewayInventoryFetches;
3010
+ if (!current) {
3011
+ current = createInventoryFetchHub();
3012
+ Object.defineProperty(globalThis, "__openworkGatewayInventoryFetches", {
3013
+ value: current,
3014
+ writable: false,
3015
+ configurable: false,
3016
+ enumerable: false
3017
+ });
3018
+ }
3019
+ return current;
3020
+ }
3021
+ function inventoryRequestTicket() {
3022
+ return hub().ticket();
3023
+ }
3024
+ function fetchSharedGatewayInventory(request) {
3025
+ return hub().fetch(request);
3026
+ }
3027
+
2947
3028
  // ../../packages/opencode-plugin/src/desktop.ts
2948
3029
  var CACHE_KEY = "desktop/provider-inventory";
2949
3030
  var digest = (value) => createHash2("sha256").update(JSON.stringify(value)).digest("hex");
@@ -3100,9 +3181,10 @@ function createDesktopProviderPlugin(deps = {}) {
3100
3181
  const accountKey = key;
3101
3182
  const generation = epoch;
3102
3183
  const current = () => !lifetime.signal.aborted && generation === epoch;
3184
+ const ticket = inventoryRequestTicket();
3103
3185
  running = (async () => {
3104
3186
  try {
3105
- const gateway = await fetchGatewayInventory(inventoryFetch(source), inventorySession(source), refreshController.signal, "desktop");
3187
+ const gateway = await fetchSharedGatewayInventory({ fetch: inventoryFetch(source), session: inventorySession(source), scope: "desktop", ticket, signal: refreshController.signal });
3106
3188
  if (!current())
3107
3189
  return;
3108
3190
  const next = { version: 1, connectionKey: accountKey, apiBaseUrl: source.apiBaseUrl, fetchedAt: now(), gateway, mcp: null };
@@ -3662,6 +3744,7 @@ function createPlugin(deps = {}) {
3662
3744
  });
3663
3745
  };
3664
3746
  const refreshOnce = async (force) => {
3747
+ const ticket = inventoryRequestTicket();
3665
3748
  const epoch = generation;
3666
3749
  const current = () => !disposed && epoch === generation;
3667
3750
  const connection = await ctx.integration.connection.active(INTEGRATION_ID).catch(() => {
@@ -3744,7 +3827,7 @@ function createPlugin(deps = {}) {
3744
3827
  return;
3745
3828
  syncStream(session, liveProviderSync);
3746
3829
  const [gateway, mcp] = await Promise.all([
3747
- options.providers ? fetchGatewayInventory(fetcher, session, refreshController.signal).then((inventory2) => ({ providers: inventory2.providers, apiKey: inventory2.apiKey })) : Promise.resolve(null),
3830
+ options.providers ? fetchSharedGatewayInventory({ fetch: fetcher, session, scope: "den", ticket, signal: refreshController.signal }).then((inventory2) => ({ providers: inventory2.providers, apiKey: inventory2.apiKey })) : Promise.resolve(null),
3748
3831
  options.mcp ? (async () => {
3749
3832
  const previous = sameAccount?.mcp?.token ?? null;
3750
3833
  const token = previous && !mcpTokenNeedsRenewal(previous, now()) ? previous : await mintMcpToken(fetcher, session);