AnyMath 文档

用于访问的外部编程接口 AnyMath

AnyMath 为第三方应用程序的开发人员提供编程接口(HTTP API),允许第三方应用程序登录到 AnyMath 并在系统中代表用户执行操作 AnyMath .

访问HTTP API AnyMath 第三方应用程序必须代表用户获得授权。 支持两种相等的授权方法:

通过标准OAuth2.0; *通过用户在其个人帐户中生成的个人访问令牌(PAT) *AnyMath .

在这两种情况下,与API的进一步交互都以相同的方式执行—使用标头。 授权:Bearer<token>.

通过OAuth2.0授权

OAuth用于需要交互式用户授权的情况(例如,集成web或桌面应用程序时)。

准备工作

第三方应用程序的开发人员向开发人员提交 AnyMath 以下资料:

  • 重定向_uri -授权后用户将被重定向到的第三方应用程序中的URL。

开发人员 AnyMath 将其传输给第三方应用程序的开发人员:

  • 客户端_id -应用程序ID;

  • 客户密码 -应用程序的密钥;

  • 范围 -允许的Api列表。

授权程序

  1. 第三方应用程序将用户重定向到 授权URL (https://engee.com/account/authorize) 具有以下查询参数:

  2. 确认访问后,用户将被重定向到 重定向_uri 带参数:

  3. 要接收令牌,您必须发送 职位-要求 令牌URL (https://engee.com/account/api/oauth2/token) 与身体 应用程序/x-www-form-url编码:

    • grant_type=authorization_code;

    • 密码 -在上一步中获得的代码;

    • 客户端_id;

    • 客户密码;

    • 范围;

    • 重定向_uri.

      请求示例:

      curl -d grant_type=authorization_code \
           -d code=$code \
           -d client_id=$client_id \
           -d client_secret=$client_secret \
           -d "scope=user:id user:username profile:name profile:primary_email engee" \
           -d redirect_uri=$redirect_uri \
           https://engee.com/account/api/oauth2/token

响应返回:

  • access_token -用于请求的授权;

  • refresh_token -用于更新访问令牌。

访问令牌的生存期为10分钟,刷新令牌为30天。

通过个人访问令牌(PAT)进行授权

个人访问令牌(PAT)是为交互式场景而设计的 不需要用户授权(脚本、CI/CD、服务器集成)。

PAT由用户在其个人帐户中手动创建 AnyMath 然后使用 类似于通过OAuth获得的访问令牌。

拍拍

  1. 打开 个人帐户 AnyMath.

  2. 转到部分*通信和安全*→个人访问令牌。

    pat 1 cn

  3. 单击*添加新令牌*。

  4. 指定:

    令牌名称*; 描述*(可选); 有效期*; 权限*(例如,API访问 AnyMath).

    pat 2 cn

  5. 单击*添加个人访问令牌*。

  6. 复制生成的令牌。

    pat 3 cn

    要复制,请使用按钮 pat copy,供浏览 — pat see.

创建后立即保存令牌。 无法在界面中重新查看。

使用PAT

在所有HTTP API请求中 AnyMath 添加标题:

Authorization: Bearer <PAT>

请求示例:

curl -H "Authorization: Bearer $pat" \
     https://engee.com/account/api/engee/info

在*开始会话之前生成个人访问令牌* AnyMath (按下按钮前 «开始» 在您的个人帐户中)。

如果会话已经启动,为了令牌正常工作,您必须:

或重新启动会话(*停止→开始); *或者在下次启动之前创建令牌。

不遵守此条件可能会导致授权错误。

互动与 AnyMath

对于所有Api进行交互 AnyMath 用户需要系统正在运行(按下开始按钮 AnyMath 在您的个人帐户中)。 如果无法按下启动按钮,那么您也可以自动启动和停止。 AnyMath (见管理方法说明 AnyMath 下一个)。

必须将authorization标头"Authorization:Bearer$token"添加到所有请求中(该令牌是通过OAuth获得的或作为个人访问令牌创建的)。

API方法

所有管理Api的前缀 AnyMath — https://engee.com.

管理层 AnyMath

获取/帐户/api/工程师/信息 -获取状态信息 AnyMath . 响应体:

{
  "username": "string",
  "serverName": "string",
  "serverId": "string",
  "url": "string",
  "serverStatus": "starting" | "running" | "stopping" | "stopped",
  "serverReady": true,
  "serverReadyAt": "2026-09-08T12:44:12.623777",
  "startingAt": "2026-09-08T12:43:39.907710",
  "startedAt": "2026-09-08T12:43:57.307400",
  "stoppingAt": "2026-09-08T13:29:57.011774",
  "stoppedAt": "2026-09-08T13:29:57.093006",
  "lastActivityAt": "2026-09-08T13:05:13.698034",
  "inactivityTimeout": 600.0,
  "stopSessionAt": null,
  "minimalMode": false,
  "profileOrganisationLicense": {...},
  "productVersion": {...},
  "clusterNamespace": {...},
  "warnings": []
}

邮递/帐户/api/工程师/开始 -发射 AnyMath (类似于按下开始按钮 AnyMath 在您的个人帐户中)。 响应正文(正在启动的服务器的URL):

{
'server': string
}

删除/帐户/api/引擎/停止 -停止 AnyMath . 返回响应代码204。

所有其他Api的前缀具有以下形式: https://engee.com/prod/user/$许可证-nickname昵称/. 每个用户都有自己的前缀,必须从响应中获取。 AnyMath 从URL字段。

任意代码的执行

POST/外部/命令/eval

  1. 最简单的请求(请求主体是JSON)。

    请求必须与标头一起发送 内容类型:应用程序/json.

    请求:

    {
      "command": "3 + 5"
    }

    返回的数据:

    *状态代码:200。

    {
      "result": "8"
    }
  2. 执行源代码时发生错误。 请求:

    {
      "command": "unexisting_variable"
    }

    返回的数据:

    *状态代码:400。

    {
      "result_code": "command_error",
      "error": {
        "full_description": "UndefVarError: `unexisting_variable` not defined\n\nStacktrace:\n  [1] top-level scope\n    @ none:1\n  [2] eval(m::Module, e::Any)\n    @ Core ./boot.jl:370\n  [3] top-level scope\n    @ none:4\n  [4] eval\n    @ ./boot.jl:370 [inlined]\n  [5] eval_mask_code(code::String)\n    @ Main.Masks /app/IJulia/preload_cells/masks.jl:206\n  [6] eval_mask_codes(mask_codes::Vector{Main.Masks.MaskCode})\n    @ Main.Masks /app/IJulia/preload_cells/masks.jl:197\n  [7] |>\n    @ ./operators.jl:907 [inlined]\n  [8] evaluate_mask_codes(mask_codes::Vector{Dict{String, String}})\n    @ Main.Masks /app/IJulia/preload_cells/masks.jl:272\n  [9] |>\n    @ ./operators.jl:907 [inlined]\n [10] (::Workspaces.Servers.var\"#eval_mask_codes#157\"{typeof(Main.Masks.evaluate_mask_codes), Workspaces.Servers.var\"#convert_mask_result#156\"})(blocks::Vector{Dict{String, String}})\n    @ Workspaces.Servers /usr/local/julia-1.9.3/packages/Workspaces/2XYbD/src/servers/handlers.jl:539\n [11] request_pipeline(req::HTTP.Messages.Request, log_func::var\"#13#14\"{String}, req_params_func::Function, log_response_func::Workspaces.Servers.var\"#mask_log_and_response#159\"{Workspaces.Servers.var\"#result_to_response#158\"}, pipeline_stages::Workspaces.Servers.PipelineStages)\n    @ Workspaces.Servers /usr/local/julia-1.9.3/packages/Workspaces/2XYbD/src/servers/handlers_common.jl:282\n [12] required_body_request_pipeline\n    @ /usr/local/julia-1.9.3/packages/Workspaces/2XYbD/src/servers/handlers_common.jl:289 [inlined]\n [13] eval_mask_code_handler\n    @ /usr/local/julia-1.9.3/packages/Workspaces/2XYbD/src/servers/handlers.jl:556 [inlined]\n [14] (::Workspaces.Servers.var\"#eval_mask_code#186\"{var\"#13#14\"{String}, typeof(Main.Masks.evaluate_mask_codes)})(req::HTTP.Messages.Request)\n    @ Workspaces.Servers /usr/local/julia-1.9.3/packages/Workspaces/2XYbD/src/servers/Servers.jl:68\n [15] (::HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing})(req::HTTP.Messages.Request)\n    @ HTTP.Handlers /usr/local/julia-1.9.3/packages/HTTP/sJD5V/src/Handlers.jl:439\n [16] (::HTTP.Handlers.var\"#1#2\"{HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}})(stream::HTTP.Streams.Stream{HTTP.Messages.Request, HTTP.Connections.Connection{Sockets.TCPSocket}})\n    @ HTTP.Handlers /usr/local/julia-1.9.3/packages/HTTP/sJD5V/src/Handlers.jl:58\n [17] #invokelatest#2\n    @ ./essentials.jl:819 [inlined]\n [18] invokelatest\n    @ ./essentials.jl:816 [inlined]\n [19] handle_connection(f::Function, c::HTTP.Connections.Connection{Sockets.TCPSocket}, listener::HTTP.Servers.Listener{Nothing, Sockets.TCPServer}, readtimeout::Int64, access_log::Nothing)\n    @ HTTP.Servers /usr/local/julia-1.9.3/packages/HTTP/sJD5V/src/Servers.jl:469\n [20] macro expansion\n    @ /usr/local/julia-1.9.3/packages/HTTP/sJD5V/src/Servers.jl:401 [inlined]\n [21] (::HTTP.Servers.var\"#16#17\"{HTTP.Handlers.var\"#1#2\"{HTTP.Handlers.Router{typeof(HTTP.Handlers.default404), typeof(HTTP.Handlers.default405), Nothing}}, HTTP.Servers.Listener{Nothing, Sockets.TCPServer}, Set{HTTP.Connections.Connection}, Int64, Nothing, ReentrantLock, Base.Semaphore, HTTP.Connections.Connection{Sockets.TCPSocket}})()\n    @ HTTP.Servers ./task.jl:514",
        "short_description": "UndefVarError(:unexisting_variable)",
        "type": "UndefVarError"
      }
    }

    这里:

    • 错误。完整描述 -错误的完整描述以及堆栈跟踪。 输出类似于错误输出到终端;

    • 错误。短描述 -无堆栈跟踪的异常;

    • 错误。类型 -错误类型。

  3. JSON返回。 请求:

    {
      "command": "using JSON\nJSON.json(\"a\"=>1)"
    }

    返回的数据:

    *状态代码:200。

    {
      "result": "{\"a\":1}"
    }

上传文件

发布/外部/文件/上传

请求以格式发送 多部分/表单-数据.

领域 路径 包含以下格式的JSON:

{
  "sources": [
    "/user/some_dir"
  ]
}

这里:

*里面的每个元素 资料来源 指示将上载文件的目录。; *领域 upload_files 包含需要上传的文件; *字段数 upload_files 它必须与传输的目录数量相匹配。

请求示例:

curl -X 'POST' \
  'https://engee.com/prod/user/$license-$user/external/file/upload' \
  -H 'Authorization: Bearer $access_token' \
  -H 'Content-Type: multipart/form-data' \
  -F 'paths={
  "sources": [
    "/user/some_directory"
  ]
}' \
  -F 'upload_files=@2.png;type=image/png'

*如果上传文件的目录不存在,则会创建该目录。; *所有下载的文件必须位于目录内 /用户 否则,将返回错误403; *如果文件已经存在,则会在其名称中添加后缀。; *文件只有在完全下载后才会写入磁盘。 如果下载中断,文件将不会出现在文件系统中。

下载文件

帖子/外部/文件/下载

请求:

{
  "sources": [
    "/user/some_file"
  ]
}

回答:

*如果请求单个文件,服务器将返回响应。 多部分/表单-数据 有一个附件:

+

  • Content-Type:multipart/form-data;boundary=。.. 响应包含文件的二进制内容的一部分。 下载文件的名称在部件的标题中传递 内容处置:表单数据;名称="文件";文件名="<文件名>".

*如果请求多个文件,服务器将返回一个ZIP存档。:

+

  • 内容类型:应用程序/x-zip压缩 **响应主体是一个包含所有请求的文件的二进制ZIP(同时保持相对于 /用户).

错误:

*如果至少有一个文件不存在 — 404未找到. *所有下载的文件必须位于目录内 /用户 否则,错误将返回 403禁止.

异步代码执行

要执行执行时间可能超过HTTP连接超时的代码,请使用异步API。 这种方法避免了在执行长期操作时断开连接。

同步和异步执行之间的选择:

*使用 POST/外部/命令/eval 当需要立即响应时,快速执行短命令.

*使用异步API(在创建后台任务部分中描述)进行长期计算,您可以稍后检查结果。

创建后台任务

职位/外部/命令/职位/创建

在后台启动代码执行。 服务器接受任务并立即返回响应,而无需等待代码执行完成。

请求主体(application/json):

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "job_type": "EVAL",
  "code": "a = 5; sleep(60); a * 2",
  "name": "Мой расчет"
}

参数:

  • 工作_id --唯一任务标识符(UUID)。

  • 作业类型 --任务类型("EVAL").

  • 密码 --要执行的代码。

  • 姓名 --可选任务名称,便于识别。

答案:

  • 202已接受 --任务已成功创建并排队等待执行。

  • 409冲突 --指定的任务 工作_id 它已经存在。

    如果您发送一个现有的请求 工作_id,然后系统会返回状态 409冲突 它不会创建任务的副本。

检查进度状态

获取/外部/命令/作业/{job_id}/结果

允许您获取任务的当前状态及其结果(当任务完成时)。

回答:

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Мой расчет",
  "job_type": "EVAL",
  "status": "IN_PROGRESS",
  "start_time": "2024-05-20T12:00:00Z",
  "end_time": null,
  "result": null
}

响应字段:

  • 状况 --完成情况: "IN_PROGRESS" (进行中)或 "完成" (完成)。

  • 开始时间 --任务的开始时间。

  • 结束时间 --任务完成时间(填写时状态= "完成").

  • 结果 --包含执行结果或错误(填写时 状态="完成").

成功完成后的结果格式:

{
  "type": "success",
  "data": {
    "text": "10"
  }
}

执行错误时的结果格式:

{
  "type": "error",
  "data": {
    "type": "UndefVarError",
    "short_description": "UndefVarError(:some_variable)",
    "full_description": "UndefVarError: `some_variable` not defined\n\nStacktrace:\n..."
  }
}

答案:

  • 200好 --已收到有关任务的信息。

  • 404未找到 --指定的任务 工作_id 没有找到。

获取问题列表

获取/外部/命令/作业/列表

检索用户创建的所有任务的列表。

回答:

{
  "jobs": [
    {
      "job_id": "16763be4-6022-406e-a950-fcd5018633ca",
      "name": "task_1",
      "job_type": "EVAL",
      "status": "DONE",
      "start_time": "2024-05-20T12:00:00Z",
      "end_time": "2024-05-20T12:05:00Z",
      "result": {
        "type": "success",
        "data": {
          "text": "Результат выполнения..."
        }
      }
    }
  ]
}

响应包含具有单个字段的对象 工作机会 --一系列任务。 将为每个任务返回完整信息,包括:

*唯一标识符(工作_id). *执行状态(状况). *开始和结束时间戳。 *执行的结果(如果任务完成)。