SuperAPI AI Directory

html-api-sdk

Coding & development

View repository

Updated 10-15 21:35

About this skill

window.Magic API — HTML Micro-App Guide

How to Use This Document

Important Constraints

  1. All window.Magic.* APIs are pre-injected — no imports needed. External CDN allowed.
  2. File paths are relative to app root (index.html dir) by default. ../ is forbidden. Use leading-slash paths such as "/shared/data.json" to access project-root files. Writing, deleting, moving, or renaming files outside the app root triggers host confirmation.
  3. window.Magic.llm tokens hosted; no api_key in HTML.
  4. No inline event handlers — use addEventListener.
  5. LLM calls must include model selector UI unless user specifies model. Default "auto".
  6. Complex file-based AI → use createTopicAndSend + @file + companion skill. Simple → readFile + llm.chat/stream.
  7. User info is privacy-gated — window.Magic.user.getInfo() returns only name and avatar by default. Sensitive fields require app.json.permissions.userInfo.scopes, a matching runtime getInfo({ scopes, reason }) request, and user confirmation.
  8. Use app.json as the micro-app manifest — every new HTML micro-app folder should include app.json next to index.html. Put type, name, entry, file aliases, watch hints, and permissions there. Do not generate magic.project.js for new HTML micro-apps.
    {
      "version": "1.0.0",
      "type": "micro-app",
      "name": "App Name",
      "entry": "index.html",
      "files": {},
      "watch": [],
      "permissions": {}
    }
    

1. File System (window.Magic.fs)

readFile(path) → Promise<string>

const raw = await window.Magic.fs.readFile("data/users.json");
const users = JSON.parse(raw);
  • path: string — relative to app root. Max 5 MB; rejects if not found.

writeFile(path, content) → Promise<void>

await window.Magic.fs.writeFile(
  "data/users.json",
  JSON.stringify(data, null, 2),
);
// Binary (up to 500 MB):
await window.Magic.fs.writeFile("data/large.bin", blob);
  • content: string | Blob | ArrayBuffer. String max 5 MB. Auto-creates dirs. ../ blocked.

⚠️ Paths relative to index.html dir, NOT workspace root.

File Paths and Project-Root Access

By default, relative window.Magic.fs.* paths resolve inside the app folder next to index.html. Use a leading slash for project-root paths. Do not add a file-scope permission block to app.json; file access scope is controlled by path syntax and host confirmation for writes outside the app root.

Path rules:

  • "data/config.json" -> app root, e.g. my-app/data/config.json.
  • "/shared/config.json" -> project root.
  • "/" lists project-root entries.
  • ../ remains blocked in all scopes.
  • Writing, deleting, moving, or renaming files outside the app root triggers host confirmation and may be rejected by the user.
  • Reading and listing project-root files do not require an app.json file-scope declaration.

listFiles(dir?) → Promise<string[]>

const files = await window.Magic.fs.listFiles("data/");

deleteFile(path) → Promise<void>

await window.Magic.fs.deleteFile("data/temp.json");
  • Rejects if file not found. ../ blocked.

deleteDir(path) → Promise<void>

await window.Magic.fs.deleteDir("temp/");
  • Recursively deletes all files and subdirectories. Cannot delete app root or project root. Rejects if dir not found. ../ blocked.

moveFile(path, targetDir) → Promise<void>

await window.Magic.fs.moveFile("data/old.json", "archive/");
  • Moves a file or directory to the specified target parent directory. Rejects if source file or target directory not found. ../ blocked.

renameFile(path, newName) → Promise<void>

await window.Magic.fs.renameFile("data/draft.txt", "final.txt");
  • Renames a file or directory. newName is just the new name (no path separators). Rejects if file not found. ../ blocked.

watchFile(path, cb) → () => void

const unwatch = window.Magic.fs.watchFile("data/orders.json", async (e) => {
  const fresh = JSON.parse(await window.Magic.fs.readFile("data/orders.json"));
  renderTable(fresh);
});
  • Polls ~3s; max 10 watched paths per app. Call returned fn to stop.

Concurrent Reads

const [users, orders] = await Promise.all([
  window.Magic.fs.readFile("data/users.json").then(JSON.parse),
  window.Magic.fs.readFile("data/orders.json").then(JSON.parse),
]);

1.5 getAppBasePath() → Promise<string>

const basePath = await window.Magic.getAppBasePath();
// "个人财务记账/" or "" (workspace root)
  • fs.* paths → relative to app root by default: "data/file.json"; project-root paths use a leading slash such as "/shared/file.json".
  • @file mention file_path → prefix: basePath + "data/file.json"
  • .magic/ paths → use as-is (already workspace root)

2. LLM API (window.Magic.llm)

getModels() → Promise<Model[]>

const models = await window.Magic.llm.getModels();
// [{id, object?, owned_by?, icon?, label?, info?}]

⚠️ model field required — default "auto". Empty string forbidden. Model selector UI must have "Auto Select" as first/default item.

chat(messages, options?) → Promise<string>

const reply = await window.Magic.llm.chat(
  [{ role: "user", content: "How many planets?" }],
  { model: "auto" },
);

Options: model (required), temperature? (0-2), maxTokens?, systemPrompt?. Timeout: 120s.

stream(messages, onChunk, options?) → () => void

let text = "";
const cancel = window.Magic.llm.stream(
  [{ role: "user", content: "Write about AI." }],
  (delta, done) => {
    text += delta;
    if (done) console.log("Done");
  },
  { model: "auto", maxTokens: 1000 },
);

onChunk: (delta: string, done: boolean) => void. Returns cancel fn.


3. Agent Interaction

setInputMessage(msg) → void

window.Magic.setInputMessage("Analysis complete. Please generate charts.");

reload() → void

window.Magic.reload();

4. Agent Namespace (window.Magic.agent)

getAgents() → Promise<AgentInfo[]>

const agents = await window.Magic.agent.getAgents();
// [{id, name, icon, color, type: "official"|"custom"|"public"}]

5. Project Namespace (window.Magic.project)

5.1 uploadFiles(files) → Promise<unknown>

Prefer fs.writeFile(path, blob) for single files.

await window.Magic.project.uploadFiles(
  files.map((f) => ({ file: f, path: `./${f.name}`, filename: f.name })),
);

Max 500 MB per file.

5.2 downloadFiles(paths) → Promise<unknown>

await window.Magic.project.downloadFiles(["output/report.pdf"]);

5.3 addFilesToMessage(filePaths, agentMode?) → Promise<unknown>

await window.Magic.project.addFilesToMessage(["data/report.csv"]);

5.4 createTopicAndSend(message, options?) → Promise<{topicId}>

Creates new topic. message: plain text or tiptap JSON (see tiptap ref).

// Plain text
const { topicId } = await window.Magic.project.createTopicAndSend(
  "Analyze this",
  { model: "auto" },
);

// Tiptap JSON with @file mention (trigger companion skill)
const { topicId: t2 } = await window.Magic.project.createTopicAndSend(
  {
    type: "doc",
    content: [
      {
        type: "paragraph",
        content: [
          { type: "text", text: "请阅读技能文件并执行:" },
          {
            type: "mention",
            attrs: {
              type: "project_file",
              data: {
                file_id: "skill_ref",
                file_name: "SKILL.md",
                file_path: ".magic/report_writer/SKILL.md",
                file_extension: "md",
              },
            },
          },
          { type: "text", text: "\n\n任务:生成报告" },
        ],
      },
    ],
  },
  { model: "auto" },
);

Options: agentId? (defaults general mode), model? (default "auto"). Timeout: 30s.

5.5 sendMessage(message, options?) → Promise<void>

await window.Magic.project.sendMessage("Continue analyzing", { model: "auto" });

Options: model?. Timeout: 15s.


6. User Info (window.Magic.user)

getInfo(options?) → Promise<UserInfo>

Default call returns only display-safe fields:

const user = await window.Magic.user.getInfo();
// {name, avatar}
document.getElementById("avatar").src = user.avatar;

Sensitive fields require permission declaration in app.json in the same folder as index.html. app.json is the declarative manifest read by the host before authorization checks; do not declare user info scopes in magic.project.js.

{
  "name": "Profile Card",
  "permissions": {
    "userInfo": {
      "scopes": ["user.profile.name", "user.profile.identity"],
      "reason": "Display the current user's profile"
    }
  }
}

Then request the declared scopes at runtime:

try {
  const user = await window.Magic.user.getInfo({
    scopes: ["user.profile.name", "user.profile.identity"],
    reason: "Display the current user's profile",
  });
  // {name, avatar, nickname, real_name, user_id, magic_id}
} catch (err) {
  // Rejected when scopes are undeclared or the user denies authorization.
}
Scope Returned fields Authorization
user.profile.display name, avatar No prompt; default
user.profile.name nickname, real_name Requires declaration and user confirmation
user.profile.identity user_id, magic_id Requires declaration and user confirmation
user.profile.organization organization_code Requires declaration and user confirmation
Field Type Description
name string Display name (real_name > nickname)
avatar string Avatar URL
nickname string Nickname; only with user.profile.name
real_name string Real name; only with user.profile.name
user_id string User ID in current org; only with user.profile.identity
magic_id string Global unique ID; only with user.profile.identity
organization_code string Current org code; only with user.profile.organization

Notes:

  • Sensitive scopes must be present in both app.json.permissions.userInfo.scopes and the runtime getInfo({ scopes }) call.
  • magic.project.js is legacy for older HTML micro-apps and still used by other project types such as slides/design/media. It is not the HTML micro-app manifest.
  • reason should explain why the app needs these fields; runtime reason overrides the app.json reason in the confirmation dialog.
  • Approved sensitive scopes are cached only for the current iframe session.
  • Never assume identity or organization fields are available from a bare getInfo() call.

Timeout: 15s.


7. Backward Compatibility

Deprecated New Path
window.Magic.getAgents() window.Magic.agent.getAgents()
window.Magic.uploadFiles(files) window.Magic.project.uploadFiles(files)
window.Magic.downloadFiles(paths) window.Magic.project.downloadFiles(paths)
window.Magic.addFilesToMessage(files) window.Magic.project.addFilesToMessage(files)
window.Magic.createTopicAndSend(msg, opts?) window.Magic.project.createTopicAndSend(msg, opts?)
window.Magic.sendMessage(msg, opts?) window.Magic.project.sendMessage(msg, opts?)

8. Error Handling

// fs: file not found
try {
  return JSON.parse(await window.Magic.fs.readFile("data/config.json"));
} catch (err) {
  if (err.message.includes("not found")) return { theme: "light" };
  throw err;
}

// llm: timeout
try {
  return await window.Magic.llm.chat(messages, { model: "auto" });
} catch (err) {
  if (err.message.includes("timed out")) return "Request timed out.";
  return "Failed: " + err.message;
}

// stream: done=true signals end (including errors)
window.Magic.llm.stream(
  messages,
  (delta, done) => {
    buffer += delta;
    if (done) finalize(buffer);
  },
  { model: "auto" },
);

9. API Quick Reference

API Returns
window.Magic.getAppBasePath() Promise<string>
window.Magic.fs.readFile(path) Promise<string>
window.Magic.fs.writeFile(path, content) Promise<void>
window.Magic.fs.listFiles(dir?) Promise<string[]>
window.Magic.fs.deleteFile(path) Promise<void>
window.Magic.fs.deleteDir(path) Promise<void>
window.Magic.fs.moveFile(path, targetDir) Promise<void>
window.Magic.fs.renameFile(path, newName) Promise<void>
window.Magic.fs.watchFile(path, cb) () => void
window.Magic.llm.getModels() Promise<Model[]>
window.Magic.llm.chat(msgs, opts?) Promise<string>
window.Magic.llm.stream(msgs, onChunk, opts?) () => void
window.Magic.setInputMessage(msg) void
window.Magic.reload() void
window.Magic.agent.getAgents() Promise<AgentInfo[]>
window.Magic.project.uploadFiles(files) Promise<unknown>
window.Magic.project.downloadFiles(paths) Promise<unknown>
window.Magic.project.addFilesToMessage(files) Promise<unknown>
window.Magic.project.createTopicAndSend(msg, opts?) Promise<{topicId}>
window.Magic.project.sendMessage(msg, opts?) Promise<void>
window.Magic.user.getInfo(options?) Promise<UserInfo>