@aiti-ai/aiti-devtools-vite 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +38 -2
- package/dist/astro.js +137 -0
- package/dist/index.js +112 -0
- package/package.json +35 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aiti-ai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @aiti-ai/aiti-devtools-vite
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Vite 载体插件:dev server 内置桥/MCP(同源 /aiti/)+ launcher 自动注入 + 启动自动注册 agent 工具;SvelteKit 等任意 Vite 工程同样适用;dev-only,生产构建零注入。
|
|
4
|
+
|
|
5
|
+
## 来源 · Origin
|
|
6
|
+
|
|
7
|
+
本包是 **aiti** 生态的组成部分——以 [pi](https://pi.dev) 为引擎的个人 AI 开发工作台与本地调试注入生态:**aiti-agent**(自托管 Web 工作台,基于开源项目 [pi-web](https://github.com/aiti-ai/pi-web) 构建,浏览器为主操作面)+ 各开发工具内的调试注入(aiti-devtools)+ 丰富的扩展体系。
|
|
8
|
+
|
|
9
|
+
This package is part of the **aiti** ecosystem — a personal AI development workbench powered by [pi](https://pi.dev): **aiti-agent** (a self-hosted web workbench built on the open-source [pi-web](https://github.com/aiti-ai/pi-web)) plus debug injection into mainstream dev tools (aiti-devtools) and a rich extension system.
|
|
10
|
+
|
|
11
|
+
- 工作台 / workbench: <https://github.com/aiti-ai/pi-web>
|
|
12
|
+
- 生态仓库 / monorepo: <https://github.com/aiti-ai/aiti-ai>
|
|
13
|
+
- 官网 / site: <https://aiti.pw>
|
|
14
|
+
|
|
15
|
+
## 功能定位(中文)
|
|
16
|
+
|
|
17
|
+
Vite 载体插件:dev server 内置桥/MCP(同源 /aiti/)+ launcher 自动注入 + 启动自动注册 agent 工具;SvelteKit 等任意 Vite 工程同样适用;dev-only,生产构建零注入。
|
|
18
|
+
|
|
19
|
+
## Positioning (English)
|
|
20
|
+
|
|
21
|
+
Vite carrier plugin: dev-server-embedded bridge/MCP (same-origin /aiti/) + automatic launcher injection + MCP auto-registration at startup; works for SvelteKit and any Vite project; dev-only, zero injection in production builds.
|
|
22
|
+
|
|
23
|
+
## 安装 / 部署
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install -D @aiti-ai/aiti-devtools-vite
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
不同载体的接线方式见文档(多数一行接入)。
|
|
30
|
+
|
|
31
|
+
## 文档 · Documentation
|
|
32
|
+
|
|
33
|
+
- 中文完整文档(aiti-docs): <https://aiti.pw/devtools/integrations/vite.html>
|
|
34
|
+
- English docs: <https://aiti.pw/en/devtools/integrations/vite.html>
|
|
35
|
+
- 扩展注册表 / registry: <https://aiti.pw/extensions.html> · machine-readable <https://aiti.pw/extensions.json>
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
MIT
|
package/dist/astro.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { createBridge, connectBridge, resolveEndpoints, registerMcp, loadAssets, htmlInjectMiddleware } from "@aiti-ai/aiti-devtools-core";
|
|
6
|
+
function hashOf(content) {
|
|
7
|
+
return createHash("sha1").update(content).digest("hex").slice(0, 8);
|
|
8
|
+
}
|
|
9
|
+
function originOf(server) {
|
|
10
|
+
const local = server.resolvedUrls?.local?.[0];
|
|
11
|
+
if (local) return local.replace(/\/+$/, "");
|
|
12
|
+
const addr = server.httpServer?.address();
|
|
13
|
+
const port = typeof addr === "object" && addr ? addr.port : server.config.server.port;
|
|
14
|
+
return `http://localhost:${port ?? 5173}`;
|
|
15
|
+
}
|
|
16
|
+
function matchPatterns(file, patterns) {
|
|
17
|
+
if (!patterns || !patterns.length) return true;
|
|
18
|
+
return patterns.some((p) => p instanceof RegExp ? p.test(file) : file.includes(p));
|
|
19
|
+
}
|
|
20
|
+
function aitiDevtools(opts = {}) {
|
|
21
|
+
const prefix = (opts.prefix || "/aiti").replace(/\/+$/, "") || "/aiti";
|
|
22
|
+
const name = opts.name || "aiti-devtools";
|
|
23
|
+
const log = opts.log || ((l) => console.log(l));
|
|
24
|
+
const dataDir = opts.dataDir || process.env.AITI_BRIDGE_DATA_DIR || join(homedir(), ".pi", "aiti-bridge");
|
|
25
|
+
const staticFiles = loadAssets(opts.assetsDir);
|
|
26
|
+
const bridge = createBridge(dataDir, { name });
|
|
27
|
+
const printBanner = (server, e) => {
|
|
28
|
+
const kindLabel = e.kind === "same-origin" ? "\u540C\u6E90\u6A21\u5F0F" : "\u5916\u90E8\u6865\u6A21\u5F0F";
|
|
29
|
+
log("");
|
|
30
|
+
log(`[${name}] \u{1F9F0} ${name}-vite \u5DF2\u542F\u52A8\uFF08${kindLabel} \xB7 name=${name}\uFF09`);
|
|
31
|
+
log(` launcher: ${e.launcherUrl}`);
|
|
32
|
+
log(` \u64CD\u4F5C\u53F0: ${e.cockpitUrl}`);
|
|
33
|
+
log(` MCP: ${e.mcpUrl} \u2190 agent \u7684 mcp \u914D\u7F6E url \u6307\u8FD9\u91CC`);
|
|
34
|
+
log(` bridge: ${e.bridgeBase}`);
|
|
35
|
+
log(` AI \u7A97: ${e.aiUrl}${opts.aiUrl ? "\uFF08aiti-agent\uFF09" : "\uFF08\u540C\u6E90 cockpit\uFF0C\u53EF\u7528 aiUrl \u6307\u5B9A aiti-agent\uFF09"}`);
|
|
36
|
+
for (const n of e.notes) log(` \xB7 ${n}`);
|
|
37
|
+
};
|
|
38
|
+
const doRegisterMcp = (server, e) => {
|
|
39
|
+
const m = opts.mcp || {};
|
|
40
|
+
if (m.register === false || m.targets === false) return;
|
|
41
|
+
const r = registerMcp({
|
|
42
|
+
url: e.mcpUrl,
|
|
43
|
+
serverName: name,
|
|
44
|
+
targets: m.targets === false ? void 0 : m.targets || "auto",
|
|
45
|
+
configPaths: m.configPaths || [],
|
|
46
|
+
projectDir: server.config.root,
|
|
47
|
+
dryRun: !!m.dryRun,
|
|
48
|
+
log
|
|
49
|
+
});
|
|
50
|
+
log(`[${name}] MCP \u6CE8\u518C\uFF1A${r.summary}`);
|
|
51
|
+
if (r.ok && !r.changed) log(`[${name}] \uFF08\u5168\u90E8\u5DF2\u662F\u6700\u65B0\uFF0C\u672A\u5199\u76D8\uFF09`);
|
|
52
|
+
};
|
|
53
|
+
const endpointsFor = (server) => resolveEndpoints({ bridgeUrl: opts.bridgeUrl, aiUrl: opts.aiUrl, serverUrl: originOf(server), prefix });
|
|
54
|
+
const mount = (server) => {
|
|
55
|
+
server.middlewares.use(connectBridge(bridge, { prefix, staticFiles }));
|
|
56
|
+
server.httpServer?.once("listening", () => {
|
|
57
|
+
try {
|
|
58
|
+
const e = endpointsFor(server);
|
|
59
|
+
printBanner(server, e);
|
|
60
|
+
doRegisterMcp(server, e);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
log(`[${name}] \u26A0 \u542F\u52A8\u540E\u5904\u7406\u5931\u8D25\uFF1A${err.message}`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
const injectHtml = (server) => {
|
|
67
|
+
if (opts.inject === false) return;
|
|
68
|
+
server.middlewares.stack.unshift({ route: "", handle: htmlInjectMiddleware({ prefix, name }) });
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
name: "@aiti-ai/aiti-devtools-vite",
|
|
72
|
+
// dev-only:生产构建(vite build)整个插件不生效——产物零注入
|
|
73
|
+
apply: "serve",
|
|
74
|
+
configureServer(server) {
|
|
75
|
+
injectHtml(server);
|
|
76
|
+
mount(server);
|
|
77
|
+
},
|
|
78
|
+
configurePreviewServer(server) {
|
|
79
|
+
injectHtml(server);
|
|
80
|
+
mount(server);
|
|
81
|
+
},
|
|
82
|
+
transformIndexHtml(html, ctx) {
|
|
83
|
+
const inject = opts.inject === false ? false : true;
|
|
84
|
+
if (!inject) return html;
|
|
85
|
+
const cfg = typeof opts.inject === "object" ? opts.inject : {};
|
|
86
|
+
const file = ctx.filename || ctx.path || "";
|
|
87
|
+
if (cfg.exclude?.length && matchPatterns(file, cfg.exclude)) return html;
|
|
88
|
+
if (cfg.include?.length && !matchPatterns(file, cfg.include)) return html;
|
|
89
|
+
const server = ctx.server;
|
|
90
|
+
const e = endpointsFor(server || {});
|
|
91
|
+
const launcher = staticFiles["/aiti-launcher.js"];
|
|
92
|
+
const v = launcher?.content ? hashOf(launcher.content) : Date.now().toString(36);
|
|
93
|
+
return [
|
|
94
|
+
{
|
|
95
|
+
tag: "script",
|
|
96
|
+
attrs: {
|
|
97
|
+
src: `${e.origin}${prefix}/aiti-launcher.js?v=${v}`,
|
|
98
|
+
"data-ai-url": e.aiUrl,
|
|
99
|
+
"data-bridge-url": e.bridgeBase,
|
|
100
|
+
"data-name": name
|
|
101
|
+
},
|
|
102
|
+
injectTo: "head-prepend"
|
|
103
|
+
}
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/astro.ts
|
|
110
|
+
function aitiAstro(opts = {}) {
|
|
111
|
+
return {
|
|
112
|
+
name: "aiti-devtools-astro",
|
|
113
|
+
hooks: {
|
|
114
|
+
"astro:config:setup"({ command, updateConfig, injectScript }) {
|
|
115
|
+
if (command !== "dev") return;
|
|
116
|
+
updateConfig({
|
|
117
|
+
vite: {
|
|
118
|
+
plugins: [aitiDevtools(opts)]
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
const prefix = opts.prefix || "/aiti";
|
|
122
|
+
const bridgeUrl = opts.bridgeUrl || prefix;
|
|
123
|
+
const aiUrl = opts.aiUrl || prefix + "/cockpit.html";
|
|
124
|
+
const name = opts.name || "aiti-devtools";
|
|
125
|
+
injectScript(
|
|
126
|
+
"head-inline",
|
|
127
|
+
`(function(){var s=document.createElement('script');s.src='${bridgeUrl}/aiti-launcher.js';s.setAttribute('data-ai-url','${aiUrl}');s.setAttribute('data-bridge-url','${bridgeUrl}');s.setAttribute('data-name','${name}');document.head.appendChild(s);})()`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
var astro_default = aitiAstro;
|
|
134
|
+
export {
|
|
135
|
+
aitiAstro,
|
|
136
|
+
astro_default as default
|
|
137
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { createBridge, connectBridge, resolveEndpoints, registerMcp, loadAssets, htmlInjectMiddleware } from "@aiti-ai/aiti-devtools-core";
|
|
6
|
+
function hashOf(content) {
|
|
7
|
+
return createHash("sha1").update(content).digest("hex").slice(0, 8);
|
|
8
|
+
}
|
|
9
|
+
function originOf(server) {
|
|
10
|
+
const local = server.resolvedUrls?.local?.[0];
|
|
11
|
+
if (local) return local.replace(/\/+$/, "");
|
|
12
|
+
const addr = server.httpServer?.address();
|
|
13
|
+
const port = typeof addr === "object" && addr ? addr.port : server.config.server.port;
|
|
14
|
+
return `http://localhost:${port ?? 5173}`;
|
|
15
|
+
}
|
|
16
|
+
function matchPatterns(file, patterns) {
|
|
17
|
+
if (!patterns || !patterns.length) return true;
|
|
18
|
+
return patterns.some((p) => p instanceof RegExp ? p.test(file) : file.includes(p));
|
|
19
|
+
}
|
|
20
|
+
function aitiDevtools(opts = {}) {
|
|
21
|
+
const prefix = (opts.prefix || "/aiti").replace(/\/+$/, "") || "/aiti";
|
|
22
|
+
const name = opts.name || "aiti-devtools";
|
|
23
|
+
const log = opts.log || ((l) => console.log(l));
|
|
24
|
+
const dataDir = opts.dataDir || process.env.AITI_BRIDGE_DATA_DIR || join(homedir(), ".pi", "aiti-bridge");
|
|
25
|
+
const staticFiles = loadAssets(opts.assetsDir);
|
|
26
|
+
const bridge = createBridge(dataDir, { name });
|
|
27
|
+
const printBanner = (server, e) => {
|
|
28
|
+
const kindLabel = e.kind === "same-origin" ? "\u540C\u6E90\u6A21\u5F0F" : "\u5916\u90E8\u6865\u6A21\u5F0F";
|
|
29
|
+
log("");
|
|
30
|
+
log(`[${name}] \u{1F9F0} ${name}-vite \u5DF2\u542F\u52A8\uFF08${kindLabel} \xB7 name=${name}\uFF09`);
|
|
31
|
+
log(` launcher: ${e.launcherUrl}`);
|
|
32
|
+
log(` \u64CD\u4F5C\u53F0: ${e.cockpitUrl}`);
|
|
33
|
+
log(` MCP: ${e.mcpUrl} \u2190 agent \u7684 mcp \u914D\u7F6E url \u6307\u8FD9\u91CC`);
|
|
34
|
+
log(` bridge: ${e.bridgeBase}`);
|
|
35
|
+
log(` AI \u7A97: ${e.aiUrl}${opts.aiUrl ? "\uFF08aiti-agent\uFF09" : "\uFF08\u540C\u6E90 cockpit\uFF0C\u53EF\u7528 aiUrl \u6307\u5B9A aiti-agent\uFF09"}`);
|
|
36
|
+
for (const n of e.notes) log(` \xB7 ${n}`);
|
|
37
|
+
};
|
|
38
|
+
const doRegisterMcp = (server, e) => {
|
|
39
|
+
const m = opts.mcp || {};
|
|
40
|
+
if (m.register === false || m.targets === false) return;
|
|
41
|
+
const r = registerMcp({
|
|
42
|
+
url: e.mcpUrl,
|
|
43
|
+
serverName: name,
|
|
44
|
+
targets: m.targets === false ? void 0 : m.targets || "auto",
|
|
45
|
+
configPaths: m.configPaths || [],
|
|
46
|
+
projectDir: server.config.root,
|
|
47
|
+
dryRun: !!m.dryRun,
|
|
48
|
+
log
|
|
49
|
+
});
|
|
50
|
+
log(`[${name}] MCP \u6CE8\u518C\uFF1A${r.summary}`);
|
|
51
|
+
if (r.ok && !r.changed) log(`[${name}] \uFF08\u5168\u90E8\u5DF2\u662F\u6700\u65B0\uFF0C\u672A\u5199\u76D8\uFF09`);
|
|
52
|
+
};
|
|
53
|
+
const endpointsFor = (server) => resolveEndpoints({ bridgeUrl: opts.bridgeUrl, aiUrl: opts.aiUrl, serverUrl: originOf(server), prefix });
|
|
54
|
+
const mount = (server) => {
|
|
55
|
+
server.middlewares.use(connectBridge(bridge, { prefix, staticFiles }));
|
|
56
|
+
server.httpServer?.once("listening", () => {
|
|
57
|
+
try {
|
|
58
|
+
const e = endpointsFor(server);
|
|
59
|
+
printBanner(server, e);
|
|
60
|
+
doRegisterMcp(server, e);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
log(`[${name}] \u26A0 \u542F\u52A8\u540E\u5904\u7406\u5931\u8D25\uFF1A${err.message}`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
const injectHtml = (server) => {
|
|
67
|
+
if (opts.inject === false) return;
|
|
68
|
+
server.middlewares.stack.unshift({ route: "", handle: htmlInjectMiddleware({ prefix, name }) });
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
name: "@aiti-ai/aiti-devtools-vite",
|
|
72
|
+
// dev-only:生产构建(vite build)整个插件不生效——产物零注入
|
|
73
|
+
apply: "serve",
|
|
74
|
+
configureServer(server) {
|
|
75
|
+
injectHtml(server);
|
|
76
|
+
mount(server);
|
|
77
|
+
},
|
|
78
|
+
configurePreviewServer(server) {
|
|
79
|
+
injectHtml(server);
|
|
80
|
+
mount(server);
|
|
81
|
+
},
|
|
82
|
+
transformIndexHtml(html, ctx) {
|
|
83
|
+
const inject = opts.inject === false ? false : true;
|
|
84
|
+
if (!inject) return html;
|
|
85
|
+
const cfg = typeof opts.inject === "object" ? opts.inject : {};
|
|
86
|
+
const file = ctx.filename || ctx.path || "";
|
|
87
|
+
if (cfg.exclude?.length && matchPatterns(file, cfg.exclude)) return html;
|
|
88
|
+
if (cfg.include?.length && !matchPatterns(file, cfg.include)) return html;
|
|
89
|
+
const server = ctx.server;
|
|
90
|
+
const e = endpointsFor(server || {});
|
|
91
|
+
const launcher = staticFiles["/aiti-launcher.js"];
|
|
92
|
+
const v = launcher?.content ? hashOf(launcher.content) : Date.now().toString(36);
|
|
93
|
+
return [
|
|
94
|
+
{
|
|
95
|
+
tag: "script",
|
|
96
|
+
attrs: {
|
|
97
|
+
src: `${e.origin}${prefix}/aiti-launcher.js?v=${v}`,
|
|
98
|
+
"data-ai-url": e.aiUrl,
|
|
99
|
+
"data-bridge-url": e.bridgeBase,
|
|
100
|
+
"data-name": name
|
|
101
|
+
},
|
|
102
|
+
injectTo: "head-prepend"
|
|
103
|
+
}
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
var index_default = aitiDevtools;
|
|
109
|
+
export {
|
|
110
|
+
aitiDevtools,
|
|
111
|
+
index_default as default
|
|
112
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiti-ai/aiti-devtools-vite",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "aiti-devtools Vite 插件:应用 dev server 上内置 MCP(launcher 注入 + 桥 + MCP 端点 + 操作台,同源 /aiti/),启动自动注册到常见 agent 工具",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js",
|
|
9
|
+
"./astro": "./dist/astro.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@aiti-ai/aiti-devtools-core": "^0.3.5"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"vite": "^6.4.3"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "esbuild src/index.ts --bundle --format=esm --outfile=dist/index.js --target=es2022 --platform=node --external:@aiti-ai/aiti-devtools-core --external:vite && esbuild src/astro.ts --bundle --format=esm --outfile=dist/astro.js --target=es2022 --platform=node --external:@aiti-ai/aiti-devtools-core"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/aiti-ai/aiti-ai.git",
|
|
30
|
+
"directory": "packages/aiti-devtools-vite"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/aiti-ai/aiti-ai#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/aiti-ai/aiti-ai/issues"
|
|
35
|
+
},
|
|
36
|
+
"contentHash": "234b8a558de186b0"
|
|
37
|
+
}
|