@limecloud/agent-app-studio 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/APP.md ADDED
@@ -0,0 +1,68 @@
1
+ ---
2
+ manifestVersion: 0.7.0
3
+ name: lime-agent-app-studio
4
+ displayName: Lime Agent App Studio
5
+ version: 0.1.1
6
+ status: preview
7
+ appType: developer-tool
8
+ description: 面向已认证开发者的 Agent App 可视化发布工作台和 npm CLI 入口。
9
+ runtimeTargets:
10
+ - local
11
+ requires:
12
+ lime:
13
+ appRuntime: ">=0.7.0 <1.0.0"
14
+ sdk: "@lime/app-sdk@^0.7.0"
15
+ capabilities:
16
+ - lime.ui
17
+ - lime.files
18
+ - lime.agent
19
+ - lime.evidence
20
+ categories:
21
+ - developer
22
+ - developer_only
23
+ - tools
24
+ publisher:
25
+ publisherId: lime-cloud
26
+ name: Lime Cloud
27
+ displayName: Lime Cloud
28
+ kind: platform
29
+ verified: true
30
+ distribution:
31
+ channel: developer-preview
32
+ visibility: developer_only
33
+ pricing: included
34
+ billingModel: none
35
+ runtimePackage:
36
+ ui:
37
+ path: ./dist/ui
38
+ entries:
39
+ - key: dashboard
40
+ kind: page
41
+ title: 发布工作台
42
+ route: /dashboard
43
+ - key: cli_quickstart
44
+ kind: page
45
+ title: CLI 快速开始
46
+ route: /cli
47
+ quickstart:
48
+ entry: dashboard
49
+ setupSteps:
50
+ - complete_developer_certification
51
+ - install_npm_cli
52
+ - run_publish_dry_run
53
+ support:
54
+ url: ./docs/v1/README.md
55
+ license: Apache-2.0
56
+ ---
57
+
58
+ # Lime Agent App Studio
59
+
60
+ Lime Agent App Studio 是开发者工具入口。已认证开发者可以在 Lime 应用中心安装它,并通过可视化工作台或 npm CLI 将 Agent App 打包、Dry-run 和发布到 LimeCore 云端。
61
+
62
+ ## CLI
63
+
64
+ ```bash
65
+ npm install -g @limecloud/agent-app-studio
66
+ lime-agent-app-studio auth status --tenant-id tenant-0001
67
+ lime-agent-app-studio publish --app-dir ./my-agent-app --channel beta --dry-run
68
+ ```
package/README.md CHANGED
@@ -17,5 +17,5 @@ lime-agent-app-studio studio --port 4177
17
17
 
18
18
  环境变量:
19
19
 
20
- - `LIMECORE_API_BASE_URL`:LimeCore API base,默认 `https://api.limecloud.run/api`
20
+ - `LIMECORE_API_BASE_URL`:LimeCore API base,默认 `https://lime-api.limeai.run/api`
21
21
  - `LIME_AGENT_APP_STUDIO_TOKEN`:开发者登录 token,CI/CD 推荐使用
package/app/app.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const fields = ["appDir", "appId", "tenantId", "apiBase", "token", "channel"];
2
2
  const output = document.querySelector("#output");
3
3
  const statusEl = document.querySelector("#status");
4
+ const defaultApiBase = "https://lime-api.limeai.run/api";
4
5
 
5
6
  function values() {
6
7
  return Object.fromEntries(fields.map((id) => [id, document.querySelector(`#${id}`).value.trim()]).filter(([, value]) => value));
@@ -9,16 +10,31 @@ function values() {
9
10
  async function post(path, body) {
10
11
  statusEl.textContent = "执行中";
11
12
  output.textContent = "请稍候...";
12
- const response = await fetch(path, {
13
- method: "POST",
14
- headers: { "Content-Type": "application/json" },
15
- body: JSON.stringify(body),
16
- });
17
- const payload = await response.json();
18
- statusEl.textContent = response.ok ? "完成" : "失败";
19
- output.textContent = JSON.stringify(payload, null, 2);
13
+ try {
14
+ const response = await fetch(path, {
15
+ method: "POST",
16
+ headers: { "Content-Type": "application/json" },
17
+ body: JSON.stringify(body),
18
+ });
19
+ const payload = await response.json();
20
+ statusEl.textContent = response.ok ? "完成" : "失败";
21
+ output.textContent = JSON.stringify(payload, null, 2);
22
+ } catch (error) {
23
+ statusEl.textContent = "需要本地服务";
24
+ output.textContent = [
25
+ "当前页面没有连接到本地 Studio 服务。",
26
+ "",
27
+ "如果你是在 Lime 应用中心里打开:",
28
+ "1. 先安装 CLI:npm install -g @limecloud/agent-app-studio",
29
+ "2. 启动本地可视化服务:lime-agent-app-studio studio --port 4177",
30
+ `3. 默认 API Base:${defaultApiBase}`,
31
+ "",
32
+ `错误:${error?.message || String(error)}`,
33
+ ].join("\n");
34
+ }
20
35
  }
21
36
 
37
+ document.querySelector("#apiBase").placeholder = defaultApiBase;
22
38
  document.querySelector("#inspectBtn").addEventListener("click", () => post("/api/inspect", values()));
23
39
  document.querySelector("#dryRunBtn").addEventListener("click", () => post("/api/publish", { ...values(), dryRun: true }));
24
40
  document.querySelector("#publishBtn").addEventListener("click", () => {
package/app/index.html CHANGED
@@ -18,7 +18,7 @@
18
18
  <label>App 目录 <input id="appDir" value="." /></label>
19
19
  <label>App ID <input id="appId" placeholder="自动识别或手动填写" /></label>
20
20
  <label>Tenant ID <input id="tenantId" placeholder="tenant-0001" /></label>
21
- <label>API Base <input id="apiBase" placeholder="https://api.limecloud.run/api" /></label>
21
+ <label>API Base <input id="apiBase" placeholder="https://lime-api.limeai.run/api" /></label>
22
22
  <label>Token <input id="token" type="password" placeholder="开发者 token" /></label>
23
23
  <label>Channel <input id="channel" value="beta" /></label>
24
24
  <div class="actions">
@@ -0,0 +1,22 @@
1
+ # Lime Agent App Studio 能力声明。
2
+ capabilities:
3
+ lime.ui:
4
+ features:
5
+ - pages
6
+ - developer-console
7
+ routes:
8
+ - path: /dashboard
9
+ component: ./dist/ui/index.html
10
+ title: 发布工作台
11
+ - path: /cli
12
+ component: ./dist/ui/index.html
13
+ title: CLI 快速开始
14
+ lime.files:
15
+ required: false
16
+ reason: 选择本地 Agent App 目录用于诊断和打包。
17
+ lime.agent:
18
+ required: false
19
+ reason: 后续版本用于生成发布前检查和修复建议。
20
+ lime.evidence:
21
+ required: false
22
+ reason: 记录 dry-run 与发布审计证据。
package/dist/ui/app.js ADDED
@@ -0,0 +1,43 @@
1
+ const fields = ["appDir", "appId", "tenantId", "apiBase", "token", "channel"];
2
+ const output = document.querySelector("#output");
3
+ const statusEl = document.querySelector("#status");
4
+ const defaultApiBase = "https://lime-api.limeai.run/api";
5
+
6
+ function values() {
7
+ return Object.fromEntries(fields.map((id) => [id, document.querySelector(`#${id}`).value.trim()]).filter(([, value]) => value));
8
+ }
9
+
10
+ async function post(path, body) {
11
+ statusEl.textContent = "执行中";
12
+ output.textContent = "请稍候...";
13
+ try {
14
+ const response = await fetch(path, {
15
+ method: "POST",
16
+ headers: { "Content-Type": "application/json" },
17
+ body: JSON.stringify(body),
18
+ });
19
+ const payload = await response.json();
20
+ statusEl.textContent = response.ok ? "完成" : "失败";
21
+ output.textContent = JSON.stringify(payload, null, 2);
22
+ } catch (error) {
23
+ statusEl.textContent = "需要本地服务";
24
+ output.textContent = [
25
+ "当前页面没有连接到本地 Studio 服务。",
26
+ "",
27
+ "如果你是在 Lime 应用中心里打开:",
28
+ "1. 先安装 CLI:npm install -g @limecloud/agent-app-studio",
29
+ "2. 启动本地可视化服务:lime-agent-app-studio studio --port 4177",
30
+ `3. 默认 API Base:${defaultApiBase}`,
31
+ "",
32
+ `错误:${error?.message || String(error)}`,
33
+ ].join("\n");
34
+ }
35
+ }
36
+
37
+ document.querySelector("#apiBase").placeholder = defaultApiBase;
38
+ document.querySelector("#inspectBtn").addEventListener("click", () => post("/api/inspect", values()));
39
+ document.querySelector("#dryRunBtn").addEventListener("click", () => post("/api/publish", { ...values(), dryRun: true }));
40
+ document.querySelector("#publishBtn").addEventListener("click", () => {
41
+ if (!confirm("正式发布会写入 LimeCore 云端 Release,确认继续?")) return;
42
+ post("/api/publish", { ...values(), publish: true });
43
+ });
@@ -0,0 +1,41 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Lime Agent App Studio</title>
7
+ <link rel="stylesheet" href="/styles.css" />
8
+ </head>
9
+ <body>
10
+ <main class="shell">
11
+ <section class="hero">
12
+ <p class="eyebrow">Lime Developer Tool</p>
13
+ <h1>Agent App 发布工作台</h1>
14
+ <p>诊断本地 Agent App,预演发布计划,并在通过开发者认证后上传到 LimeCore 云端 Release。</p>
15
+ </section>
16
+
17
+ <section class="panel form-panel">
18
+ <label>App 目录 <input id="appDir" value="." /></label>
19
+ <label>App ID <input id="appId" placeholder="自动识别或手动填写" /></label>
20
+ <label>Tenant ID <input id="tenantId" placeholder="tenant-0001" /></label>
21
+ <label>API Base <input id="apiBase" placeholder="https://lime-api.limeai.run/api" /></label>
22
+ <label>Token <input id="token" type="password" placeholder="开发者 token" /></label>
23
+ <label>Channel <input id="channel" value="beta" /></label>
24
+ <div class="actions">
25
+ <button id="inspectBtn">诊断项目</button>
26
+ <button id="dryRunBtn">Dry-run</button>
27
+ <button id="publishBtn" class="danger">正式发布</button>
28
+ </div>
29
+ </section>
30
+
31
+ <section class="panel">
32
+ <div class="panel-head">
33
+ <h2>结果</h2>
34
+ <span id="status">等待操作</span>
35
+ </div>
36
+ <pre id="output">选择本地 Agent App 目录后开始。</pre>
37
+ </section>
38
+ </main>
39
+ <script type="module" src="/app.js"></script>
40
+ </body>
41
+ </html>
@@ -0,0 +1,129 @@
1
+ :root {
2
+ --ink: #112036;
3
+ --muted: #66745f;
4
+ --card: rgba(255, 252, 244, 0.88);
5
+ --line: #dfd5bf;
6
+ --green: #2f8a54;
7
+ --blue: #14265c;
8
+ --danger: #9a4a27;
9
+ }
10
+
11
+ * { box-sizing: border-box; }
12
+ body {
13
+ margin: 0;
14
+ min-height: 100vh;
15
+ color: var(--ink);
16
+ font-family: Charter, Georgia, "Times New Roman", serif;
17
+ background:
18
+ radial-gradient(circle at 10% 10%, rgba(47, 138, 84, 0.20), transparent 28rem),
19
+ radial-gradient(circle at 90% 20%, rgba(20, 38, 92, 0.16), transparent 32rem),
20
+ linear-gradient(135deg, #fffaf0, #f4efe1 60%, #e9efdc);
21
+ }
22
+
23
+ .shell {
24
+ width: min(1120px, calc(100vw - 40px));
25
+ margin: 0 auto;
26
+ padding: 48px 0;
27
+ }
28
+
29
+ .hero {
30
+ display: grid;
31
+ gap: 12px;
32
+ max-width: 760px;
33
+ margin-bottom: 28px;
34
+ }
35
+ .eyebrow {
36
+ margin: 0;
37
+ color: var(--green);
38
+ font-weight: 800;
39
+ letter-spacing: 0.12em;
40
+ text-transform: uppercase;
41
+ }
42
+ h1 {
43
+ margin: 0;
44
+ font-size: clamp(40px, 7vw, 88px);
45
+ line-height: 0.92;
46
+ }
47
+ .hero p:last-child {
48
+ margin: 0;
49
+ color: var(--muted);
50
+ font-size: 20px;
51
+ line-height: 1.6;
52
+ }
53
+
54
+ .panel {
55
+ margin-top: 18px;
56
+ padding: 24px;
57
+ border: 1px solid var(--line);
58
+ border-radius: 28px;
59
+ background: var(--card);
60
+ box-shadow: 0 22px 70px rgba(60, 49, 25, 0.14);
61
+ backdrop-filter: blur(18px);
62
+ }
63
+ .form-panel {
64
+ display: grid;
65
+ grid-template-columns: repeat(2, minmax(0, 1fr));
66
+ gap: 16px;
67
+ }
68
+ label {
69
+ display: grid;
70
+ gap: 8px;
71
+ color: var(--muted);
72
+ font-weight: 700;
73
+ }
74
+ input {
75
+ width: 100%;
76
+ border: 1px solid var(--line);
77
+ border-radius: 16px;
78
+ padding: 13px 14px;
79
+ color: var(--ink);
80
+ background: rgba(255, 255, 255, 0.72);
81
+ font: 600 15px ui-monospace, SFMono-Regular, Menlo, monospace;
82
+ }
83
+ .actions {
84
+ grid-column: 1 / -1;
85
+ display: flex;
86
+ gap: 12px;
87
+ flex-wrap: wrap;
88
+ }
89
+ button {
90
+ border: 0;
91
+ border-radius: 999px;
92
+ padding: 13px 22px;
93
+ color: white;
94
+ background: var(--blue);
95
+ font-weight: 800;
96
+ cursor: pointer;
97
+ }
98
+ button:nth-child(1) { background: var(--green); }
99
+ button.danger { background: var(--danger); }
100
+ .panel-head {
101
+ display: flex;
102
+ justify-content: space-between;
103
+ align-items: center;
104
+ gap: 16px;
105
+ }
106
+ h2 { margin: 0; }
107
+ #status {
108
+ border: 1px solid var(--line);
109
+ border-radius: 999px;
110
+ padding: 8px 12px;
111
+ color: var(--muted);
112
+ background: rgba(255,255,255,0.72);
113
+ font-weight: 800;
114
+ }
115
+ pre {
116
+ overflow: auto;
117
+ min-height: 280px;
118
+ margin: 18px 0 0;
119
+ padding: 18px;
120
+ border-radius: 20px;
121
+ background: #101827;
122
+ color: #e9f2dc;
123
+ font-size: 13px;
124
+ line-height: 1.6;
125
+ }
126
+
127
+ @media (max-width: 760px) {
128
+ .form-panel { grid-template-columns: 1fr; }
129
+ }
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@limecloud/agent-app-studio",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Lime Agent App Studio CLI and visual publisher for Agent App packages.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -13,11 +13,17 @@
13
13
  "bin",
14
14
  "src",
15
15
  "app",
16
+ "dist",
17
+ "scripts",
18
+ "APP.md",
19
+ "app.capabilities.yaml",
16
20
  "docs/v1/README.md",
17
21
  "LICENSE",
18
22
  "README.md"
19
23
  ],
20
24
  "scripts": {
25
+ "build": "node scripts/build-agent-app.mjs",
26
+ "validate:app": "node src/cli.mjs project inspect --app-dir .",
21
27
  "test": "node --test tests/*.test.mjs",
22
28
  "pack:dry-run": "npm pack --dry-run",
23
29
  "publish:dry-run": "npm publish --access public --dry-run",
@@ -0,0 +1,14 @@
1
+ import { cp, mkdir, rm, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const root = fileURLToPath(new URL("..", import.meta.url));
6
+ const distUi = join(root, "dist", "ui");
7
+
8
+ await rm(distUi, { recursive: true, force: true });
9
+ await mkdir(join(distUi, "vendor"), { recursive: true });
10
+ for (const file of ["index.html", "app.js", "styles.css"]) {
11
+ await cp(join(root, "app", file), join(distUi, file));
12
+ }
13
+ await writeFile(join(distUi, "vendor", ".gitkeep"), "");
14
+ console.log(`已生成 Agent App UI: ${distUi}`);
@@ -7,11 +7,10 @@ import { homedir } from "node:os";
7
7
 
8
8
  const defaultConfigDir = join(homedir(), ".lime", "agent-app-studio");
9
9
  const defaultConfigPath = join(defaultConfigDir, "config.json");
10
+ const defaultApiBase = "https://lime-api.limeai.run/api";
10
11
 
11
12
  export function resolveApiBase(options = {}) {
12
- return trimTrailingSlash(
13
- options.apiBase || process.env.LIMECORE_API_BASE_URL || "https://api.limecloud.run/api"
14
- );
13
+ return trimTrailingSlash(options.apiBase || process.env.LIMECORE_API_BASE_URL || defaultApiBase);
15
14
  }
16
15
 
17
16
  export async function loadStudioConfig() {
package/src/server.mjs CHANGED
@@ -13,7 +13,7 @@ const root = fileURLToPath(new URL("..", import.meta.url));
13
13
  const appRoot = join(root, "app");
14
14
 
15
15
  export async function startStudioServer(options = {}) {
16
- const port = Number(options.port || 4177);
16
+ const port = Number(options.port ?? 4177);
17
17
  const server = createServer(async (req, res) => {
18
18
  try {
19
19
  if (req.method === "POST" && req.url === "/api/inspect") {
@@ -31,14 +31,25 @@ export async function startStudioServer(options = {}) {
31
31
  }
32
32
  });
33
33
  await new Promise((resolve) => server.listen(port, resolve));
34
- return { server, url: `http://127.0.0.1:${port}` };
34
+ const address = server.address();
35
+ const actualPort = typeof address === "object" && address ? address.port : port;
36
+ return { server, url: `http://127.0.0.1:${actualPort}` };
35
37
  }
36
38
 
37
39
  async function serveStatic(req, res) {
38
40
  const pathname = req.url === "/" ? "/index.html" : req.url.split("?")[0];
41
+ if (pathname === "/favicon.ico") return sendNoContent(res);
39
42
  const safePath = pathname.replace(/\.\./g, "");
40
43
  const filePath = join(appRoot, safePath);
41
- const content = await readFile(filePath);
44
+ let content;
45
+ try {
46
+ content = await readFile(filePath);
47
+ } catch (error) {
48
+ if (error?.code === "ENOENT" || error?.code === "EISDIR") {
49
+ return sendNotFound(res);
50
+ }
51
+ throw error;
52
+ }
42
53
  const type = contentType(filePath);
43
54
  res.writeHead(200, { "Content-Type": type });
44
55
  res.end(content);
@@ -56,6 +67,16 @@ function sendJson(res, payload, status = 200) {
56
67
  res.end(JSON.stringify(payload, null, 2));
57
68
  }
58
69
 
70
+ function sendNoContent(res) {
71
+ res.writeHead(204, { "Content-Length": "0" });
72
+ res.end();
73
+ }
74
+
75
+ function sendNotFound(res) {
76
+ res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
77
+ res.end("Not Found");
78
+ }
79
+
59
80
  function contentType(path) {
60
81
  switch (extname(path)) {
61
82
  case ".js":