agentlas 0.9.10 → 1.0.2
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/CHANGELOG.md +106 -0
- package/README.md +319 -341
- package/bin/agentlas.cjs +32 -9
- package/engine/agentlas-banner.cjs +24 -3
- package/engine/agentlas-composer.cjs +239 -31
- package/engine/agentlas-config.cjs +103 -7
- package/engine/agentlas-core-harness.cjs +48 -4
- package/engine/agentlas-evolution.cjs +3 -4
- package/engine/agentlas-i18n.cjs +88 -32
- package/engine/agentlas-input.cjs +234 -18
- package/engine/agentlas-memory-governance.cjs +3 -5
- package/engine/agentlas-memory-import.cjs +3 -3
- package/engine/agentlas-native-host.cjs +200 -9
- package/engine/agentlas-onboard.cjs +112 -20
- package/engine/agentlas-permissions.cjs +14 -7
- package/engine/agentlas-sqlite-policy.cjs +34 -0
- package/engine/agentlas-ui.cjs +93 -42
- package/engine/agentlas-workforce.cjs +472 -58
- package/engine/agentlas-workload-routing.cjs +8 -3
- package/engine/agentlas.cjs +140 -12367
- package/engine/agents/files.cjs +61 -0
- package/engine/agents/import-local.cjs +237 -0
- package/engine/agents/registry.cjs +158 -0
- package/engine/agents/router.cjs +565 -0
- package/engine/agents/routes.cjs +43 -0
- package/engine/architecture.data.json +2 -2
- package/engine/automation/daemon.cjs +335 -0
- package/engine/automation/schedule.cjs +181 -0
- package/engine/automation/store.cjs +209 -0
- package/engine/cloud/auth.cjs +279 -0
- package/engine/cloud/hub-client.cjs +239 -0
- package/engine/cloud-assets/cargo.cjs +49 -0
- package/engine/cloud-assets/cas.cjs +235 -0
- package/engine/cloud-assets/commands.cjs +273 -0
- package/engine/cloud-assets/package.cjs +936 -0
- package/engine/cloud-assets/restore.cjs +172 -0
- package/engine/cloud-assets/state.cjs +268 -0
- package/engine/commands/automation.cjs +195 -0
- package/engine/commands/billing.cjs +96 -0
- package/engine/commands/browser.cjs +20 -0
- package/engine/commands/build.cjs +33 -0
- package/engine/commands/call.cjs +24 -0
- package/engine/commands/career-graph.cjs +51 -0
- package/engine/commands/cd.cjs +22 -0
- package/engine/commands/chat.cjs +12 -0
- package/engine/commands/chats.cjs +28 -0
- package/engine/commands/cloud.cjs +17 -0
- package/engine/commands/connect.cjs +20 -0
- package/engine/commands/context.cjs +66 -0
- package/engine/commands/creds.cjs +203 -0
- package/engine/commands/doctor.cjs +68 -0
- package/engine/commands/env.cjs +33 -0
- package/engine/commands/evolve.cjs +23 -0
- package/engine/commands/experience.cjs +34 -0
- package/engine/commands/film.cjs +8 -0
- package/engine/commands/firm.cjs +115 -0
- package/engine/commands/help.cjs +65 -0
- package/engine/commands/hep.cjs +23 -0
- package/engine/commands/import.cjs +35 -0
- package/engine/commands/index.cjs +136 -0
- package/engine/commands/install.cjs +27 -0
- package/engine/commands/journal.cjs +31 -0
- package/engine/commands/legacy-network.cjs +29 -0
- package/engine/commands/list.cjs +49 -0
- package/engine/commands/login.cjs +68 -0
- package/engine/commands/logout.cjs +28 -0
- package/engine/commands/mcp.cjs +91 -0
- package/engine/commands/memory.cjs +21 -0
- package/engine/commands/multimodal.cjs +91 -0
- package/engine/commands/native.cjs +34 -0
- package/engine/commands/netadmin.cjs +31 -0
- package/engine/commands/oberon.cjs +70 -0
- package/engine/commands/ontology.cjs +24 -0
- package/engine/commands/open.cjs +48 -0
- package/engine/commands/plugin.cjs +101 -0
- package/engine/commands/project.cjs +47 -0
- package/engine/commands/research.cjs +36 -0
- package/engine/commands/route.cjs +37 -0
- package/engine/commands/run.cjs +149 -0
- package/engine/commands/search.cjs +55 -0
- package/engine/commands/setup.cjs +45 -0
- package/engine/commands/storm.cjs +75 -0
- package/engine/commands/swarm.cjs +75 -0
- package/engine/commands/telegram.cjs +32 -0
- package/engine/commands/uninstall.cjs +68 -0
- package/engine/commands/update.cjs +54 -0
- package/engine/commands/upload.cjs +18 -0
- package/engine/commands/usage.cjs +35 -0
- package/engine/commands/variant.cjs +25 -0
- package/engine/commands/version.cjs +9 -0
- package/engine/commands/whoami.cjs +38 -0
- package/engine/commands/workforce.cjs +103 -0
- package/engine/core/db.cjs +160 -0
- package/engine/core/paths.cjs +35 -0
- package/engine/experience/build.cjs +181 -0
- package/engine/experience/intents.cjs +492 -0
- package/engine/experience/runtime.cjs +242 -0
- package/engine/experience/variant.cjs +196 -0
- package/engine/firms/orchestrate.cjs +333 -0
- package/engine/hephaestus/runtime.cjs +697 -0
- package/engine/hub/install.cjs +872 -0
- package/engine/hub/plugins.cjs +213 -0
- package/engine/mcp/consent.cjs +289 -0
- package/engine/mcp/contract.cjs +202 -0
- package/engine/mcp/index.cjs +43 -0
- package/engine/mcp/inventory.cjs +322 -0
- package/engine/mcp/plan.cjs +286 -0
- package/engine/mcp/probe.cjs +151 -0
- package/engine/memory-cli/curate.cjs +163 -0
- package/engine/oberon/common.cjs +69 -0
- package/engine/oberon/manifest.cjs +164 -0
- package/engine/oberon/outputs.cjs +70 -0
- package/engine/oberon/render.cjs +164 -0
- package/engine/project/career-graph.cjs +249 -0
- package/engine/project/credentials.cjs +262 -0
- package/engine/project/env-file.cjs +46 -0
- package/engine/project/index.cjs +27 -0
- package/engine/project/memory-context.cjs +453 -0
- package/engine/project/ontology.cjs +467 -0
- package/engine/project/paths.cjs +39 -0
- package/engine/project/seed.cjs +200 -0
- package/engine/project/state.cjs +403 -0
- package/engine/project/super-ontology-seed.json +3288 -0
- package/engine/runtimes/detect.cjs +54 -0
- package/engine/runtimes/overrides.cjs +139 -0
- package/engine/runtimes/resolve.cjs +64 -0
- package/engine/sessions/apply-fences.cjs +188 -0
- package/engine/sessions/fences.cjs +362 -0
- package/engine/sessions/orchestrator.cjs +170 -0
- package/engine/sessions/prompt.cjs +212 -0
- package/engine/sessions/session.cjs +245 -0
- package/engine/sessions/sink.cjs +54 -0
- package/engine/sessions/store.cjs +79 -0
- package/engine/storm/deps.cjs +88 -0
- package/engine/storm/storm.cjs +218 -0
- package/engine/storm/swarm.cjs +422 -0
- package/engine/ui/palette.cjs +105 -0
- package/engine/ui/renderer.cjs +85 -0
- package/engine/ui/repl.cjs +444 -0
- package/engine/workforce/capture.cjs +701 -0
- package/engine/workforce/deps.cjs +472 -0
- package/package.json +2 -6
- package/engine/agentlas-experience-mcp.cjs +0 -1709
- package/engine/agentlas-parity.cjs +0 -1499
- package/engine/agentlas-repl.cjs +0 -1780
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* project/ontology — 프로젝트 온톨로지 inbox/source 상태 (v1 monolith 7654–8283 포팅).
|
|
4
|
+
*
|
|
5
|
+
* 안전 계약:
|
|
6
|
+
* - 현재 프로젝트 수신함(.agentlas/ontology-inbox)과 명시 등록 폴더만 사용.
|
|
7
|
+
* 홈 폴더·형제 프로젝트 스캔은 절대 시작하지 않는다.
|
|
8
|
+
* - status/list는 수동(비변형): 초기화 안 된 프로젝트에는 파일을 만들지 않고
|
|
9
|
+
* `agentlas project init` 안내만 한다 (0.9.10 경계).
|
|
10
|
+
* - open/add 계열만 ensureOntologyCli를 거치며, 그마저도 초기화된 프로젝트에서만
|
|
11
|
+
* manifest/inbox를 만든다 — 초기화되지 않았으면 던진다.
|
|
12
|
+
*/
|
|
13
|
+
const fs = require("node:fs");
|
|
14
|
+
const os = require("node:os");
|
|
15
|
+
const path = require("node:path");
|
|
16
|
+
const { spawn } = require("node:child_process");
|
|
17
|
+
const { loadArch } = require("../core/db.cjs");
|
|
18
|
+
const { initializedAgentlasProjectPathCli } = require("./state.cjs");
|
|
19
|
+
|
|
20
|
+
const ONTOLOGY_SUPPORTED_EXTS = new Set([".txt", ".md", ".markdown", ".json", ".csv", ".tsv"]);
|
|
21
|
+
|
|
22
|
+
/** `--key value` / `--flag` 파서 — v1 parseCloudFlags와 동일 규칙의 로컬 복사본. */
|
|
23
|
+
function parseFlagsCli(args) {
|
|
24
|
+
const flags = { _: [] };
|
|
25
|
+
for (let i = 0; i < args.length; i++) {
|
|
26
|
+
const a = args[i];
|
|
27
|
+
if (a && a.startsWith("--")) {
|
|
28
|
+
const key = a.slice(2);
|
|
29
|
+
const next = args[i + 1];
|
|
30
|
+
if (next !== undefined && !String(next).startsWith("--")) {
|
|
31
|
+
flags[key] = next;
|
|
32
|
+
i++;
|
|
33
|
+
} else {
|
|
34
|
+
flags[key] = true;
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
flags._.push(a);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return flags;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function ontologyPathsForCli(projectPath) {
|
|
44
|
+
const arch = loadArch();
|
|
45
|
+
const root = path.resolve(projectPath || process.cwd());
|
|
46
|
+
const memoryDir = path.join(root, arch.memoryDir || ".agentlas");
|
|
47
|
+
return {
|
|
48
|
+
root,
|
|
49
|
+
memoryDir,
|
|
50
|
+
configPath: path.join(memoryDir, arch.ontologyRuntimeFile || "ontology-runtime.json"),
|
|
51
|
+
sourceManifestPath: path.join(memoryDir, arch.ontologySourceManifestFile || "ontology-sources.json"),
|
|
52
|
+
inboxPath: path.join(memoryDir, arch.ontologyInboxDir || "ontology-inbox"),
|
|
53
|
+
dbPath: path.join(memoryDir, arch.ontologyDbFile || "ontology-runtime.sqlite"),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readJsonSafeCli(filePath, fallback) {
|
|
58
|
+
try {
|
|
59
|
+
if (!fs.existsSync(filePath)) return fallback;
|
|
60
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
61
|
+
} catch {
|
|
62
|
+
return fallback;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function writeJsonSafeCli(filePath, value) {
|
|
67
|
+
fs.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 원자적(temp+rename) + 소유자 전용(0600) JSON 쓰기. 세션 ID/경로 등 민감 상태 파일용:
|
|
71
|
+
// (1) 크래시 중간 쓰기로 JSON이 깨져 routesMap()이 {}를 돌려주며 임포트 매핑을 통째로 잃던 사고,
|
|
72
|
+
// (2) 기본 umask(0644)로 cli-sessions.json/agent-routes.json이 world-readable이던 정보 노출을 함께 막는다.
|
|
73
|
+
function writeJsonPrivateAtomicCli(filePath, value) {
|
|
74
|
+
const dir = path.dirname(filePath);
|
|
75
|
+
const tmp = path.join(dir, `.${path.basename(filePath)}.${process.pid}.tmp`);
|
|
76
|
+
fs.writeFileSync(tmp, JSON.stringify(value, null, 2) + "\n", { encoding: "utf8", mode: 0o600 });
|
|
77
|
+
try {
|
|
78
|
+
fs.renameSync(tmp, filePath);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
try { fs.unlinkSync(tmp); } catch { /* ignore */ }
|
|
81
|
+
throw e;
|
|
82
|
+
}
|
|
83
|
+
try { fs.chmodSync(filePath, 0o600); } catch { /* 일부 FS는 chmod 미지원 — best-effort */ }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function ontologySourceManifestSkeletonCli(root) {
|
|
87
|
+
return {
|
|
88
|
+
schemaVersion: "1.0",
|
|
89
|
+
kind: "agentlas-ontology-source-manifest",
|
|
90
|
+
projectRoot: root,
|
|
91
|
+
sources: [],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function ensureOntologyCli(projectPath, lang) {
|
|
96
|
+
const paths = ontologyPathsForCli(projectPath);
|
|
97
|
+
if (!initializedAgentlasProjectPathCli(paths.root)) {
|
|
98
|
+
throw new Error(lang === "ko"
|
|
99
|
+
? "Agentlas 프로젝트 상태가 초기화되지 않았습니다. 먼저 `agentlas project init`을 명시적으로 실행하세요."
|
|
100
|
+
: "Agentlas project state is not initialized. Run `agentlas project init` explicitly first.");
|
|
101
|
+
}
|
|
102
|
+
fs.mkdirSync(paths.inboxPath, { recursive: true });
|
|
103
|
+
if (!fs.existsSync(paths.sourceManifestPath)) {
|
|
104
|
+
writeJsonSafeCli(paths.sourceManifestPath, ontologySourceManifestSkeletonCli(paths.root));
|
|
105
|
+
}
|
|
106
|
+
return paths;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function listOntologyInboxCli(inboxPath) {
|
|
110
|
+
try {
|
|
111
|
+
if (!fs.existsSync(inboxPath)) return [];
|
|
112
|
+
return fs.readdirSync(inboxPath, { withFileTypes: true })
|
|
113
|
+
.filter((entry) => !entry.name.startsWith("."))
|
|
114
|
+
.map((entry) => {
|
|
115
|
+
const full = path.join(inboxPath, entry.name);
|
|
116
|
+
const stat = fs.statSync(full);
|
|
117
|
+
const isDir = entry.isDirectory();
|
|
118
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
119
|
+
return {
|
|
120
|
+
name: entry.name,
|
|
121
|
+
path: full,
|
|
122
|
+
kind: isDir ? "dir" : "file",
|
|
123
|
+
size: isDir ? 0 : stat.size,
|
|
124
|
+
supported: isDir || ONTOLOGY_SUPPORTED_EXTS.has(ext),
|
|
125
|
+
};
|
|
126
|
+
})
|
|
127
|
+
.slice(0, 80);
|
|
128
|
+
} catch {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function readOntologySourcesCli(sourceManifestPath) {
|
|
134
|
+
const manifest = readJsonSafeCli(sourceManifestPath, { sources: [] });
|
|
135
|
+
return Array.isArray(manifest.sources) ? manifest.sources : [];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function ontologyUsageLinesCli(lang) {
|
|
139
|
+
if (lang === "ko") {
|
|
140
|
+
return [
|
|
141
|
+
"온톨로지 명령:",
|
|
142
|
+
" /ontology 이 프로젝트의 온톨로지 상태 표시",
|
|
143
|
+
" /ontology list 수신함 파일과 등록 폴더 목록",
|
|
144
|
+
" /ontology open 프로젝트 온톨로지 수신함 열기",
|
|
145
|
+
" /ontology add ./docs 폴더를 비공개 프로젝트 지식으로 등록",
|
|
146
|
+
" /ontology company ./docs 회사 문서를 비공개로 등록",
|
|
147
|
+
" /ontology personal ~/notes 개인 문서를 비공개로 등록",
|
|
148
|
+
"",
|
|
149
|
+
"안전: 현재 프로젝트 수신함과 명시적으로 등록한 폴더만 사용합니다.",
|
|
150
|
+
"홈 폴더나 이웃 프로젝트 스캔은 시작하지 않습니다.",
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
return [
|
|
154
|
+
"Ontology commands:",
|
|
155
|
+
" /ontology turn on/show this project's ontology",
|
|
156
|
+
" /ontology list list inbox files and registered folders",
|
|
157
|
+
" /ontology open open the project ontology inbox",
|
|
158
|
+
" /ontology add ./docs register a folder as private project knowledge",
|
|
159
|
+
" /ontology company ./docs register company docs as private",
|
|
160
|
+
" /ontology personal ~/notes register personal docs as private",
|
|
161
|
+
"",
|
|
162
|
+
"Natural examples:",
|
|
163
|
+
" /ontology use ./docs as company knowledge",
|
|
164
|
+
" /ontology attach ~/notes as personal private memory",
|
|
165
|
+
" /ontology open the inbox",
|
|
166
|
+
"",
|
|
167
|
+
"Safety: only the current project inbox and registered folders are used.",
|
|
168
|
+
"No home folder or sibling project scan is started.",
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function shellSplitCli(text) {
|
|
173
|
+
const parts = [];
|
|
174
|
+
let current = "";
|
|
175
|
+
let quote = null;
|
|
176
|
+
let escaped = false;
|
|
177
|
+
for (const ch of String(text || "")) {
|
|
178
|
+
if (escaped) {
|
|
179
|
+
current += ch;
|
|
180
|
+
escaped = false;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (ch === "\\") {
|
|
184
|
+
escaped = true;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (quote) {
|
|
188
|
+
if (ch === quote) quote = null;
|
|
189
|
+
else current += ch;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (ch === "\"" || ch === "'") {
|
|
193
|
+
quote = ch;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (/\s/.test(ch)) {
|
|
197
|
+
if (current) {
|
|
198
|
+
parts.push(current);
|
|
199
|
+
current = "";
|
|
200
|
+
}
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
current += ch;
|
|
204
|
+
}
|
|
205
|
+
if (current) parts.push(current);
|
|
206
|
+
return parts;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function expandUserPathCli(value) {
|
|
210
|
+
const v = String(value || "").trim();
|
|
211
|
+
if (v === "~") return os.homedir();
|
|
212
|
+
if (v.startsWith("~/") || v.startsWith("~\\")) return path.join(os.homedir(), v.slice(2));
|
|
213
|
+
return v;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function cleanOntologyPathTokenCli(value) {
|
|
217
|
+
return String(value || "")
|
|
218
|
+
.trim()
|
|
219
|
+
.replace(/^@/, "")
|
|
220
|
+
.replace(/^[`"']+|[`"',;]+$/g, "");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function resolveOntologyPathCli(value, cwd) {
|
|
224
|
+
const clean = expandUserPathCli(cleanOntologyPathTokenCli(value));
|
|
225
|
+
return path.isAbsolute(clean) ? path.resolve(clean) : path.resolve(cwd || process.cwd(), clean);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function inferOntologyKindCli(value, text) {
|
|
229
|
+
const v = String(value || "").toLowerCase();
|
|
230
|
+
const hay = String(text || "").toLowerCase();
|
|
231
|
+
if (["company", "work", "business", "corp", "team", "회사", "업무", "팀", "조직"].includes(v) || /(company|work|business|corp|team|회사|업무|조직)/i.test(hay)) return "company";
|
|
232
|
+
if (["personal", "private-life", "life", "me", "개인", "내자료", "일상"].includes(v) || /(personal|private-life|\bme\b|개인|내\s*자료|일상)/i.test(hay)) return "personal";
|
|
233
|
+
if (["project", "repo", "프로젝트", "레포"].includes(v) || /(project|repo|프로젝트|레포)/i.test(hay)) return "project";
|
|
234
|
+
return "project";
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function inferOntologyScopeCli(value, text, kind) {
|
|
238
|
+
const v = String(value || "").toLowerCase();
|
|
239
|
+
const hay = String(text || "").toLowerCase();
|
|
240
|
+
if (["public", "open", "공개"].includes(v) || /(public|open|공개)/i.test(hay)) return "public";
|
|
241
|
+
if (["internal", "team", "내부", "팀"].includes(v) || /(internal|team-only|company-wide|내부|팀\s*공유|회사\s*공유)/i.test(hay)) return "internal";
|
|
242
|
+
if (["private", "secret", "local", "비공개", "개인"].includes(v) || /(private|secret|local-only|비공개|개인만|나만)/i.test(hay)) return "private";
|
|
243
|
+
return kind === "company" || kind === "personal" ? "private" : "private";
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function isOntologyPathishCli(token, cwd, allowExistingName) {
|
|
247
|
+
const clean = cleanOntologyPathTokenCli(token);
|
|
248
|
+
if (!clean || clean === "." || clean === "..") return true;
|
|
249
|
+
if (/^(?:~|\.{1,2}[\\/]|\/|[A-Za-z]:[\\/])/.test(clean)) return true;
|
|
250
|
+
if (clean.includes("/") || clean.includes("\\")) return true;
|
|
251
|
+
if (allowExistingName) {
|
|
252
|
+
try {
|
|
253
|
+
return fs.existsSync(resolveOntologyPathCli(clean, cwd));
|
|
254
|
+
} catch {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function findOntologyPathTokenCli(tokens, cwd, text) {
|
|
262
|
+
const lower = String(text || "").toLowerCase();
|
|
263
|
+
const addIntent = /(add|register|attach|source|folder|watch|sync|추가|등록|붙|연결|폴더|자료|문서)/i.test(lower);
|
|
264
|
+
const skip = new Set([
|
|
265
|
+
"add", "register", "attach", "source", "sources", "folder", "folders", "watch", "sync", "use",
|
|
266
|
+
"company", "personal", "project", "private", "internal", "public", "work", "business",
|
|
267
|
+
"추가", "등록", "붙여", "붙여줘", "연결", "켜줘", "켜", "회사", "개인", "프로젝트", "자료", "문서", "폴더", "비공개", "내부", "공개",
|
|
268
|
+
]);
|
|
269
|
+
for (const token of tokens) {
|
|
270
|
+
const clean = cleanOntologyPathTokenCli(token);
|
|
271
|
+
if (!clean || skip.has(clean.toLowerCase())) continue;
|
|
272
|
+
if (isOntologyPathishCli(clean, cwd, addIntent)) return clean;
|
|
273
|
+
}
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function parseOntologyNaturalArgsCli(text, cwd) {
|
|
278
|
+
const raw = String(text || "").trim();
|
|
279
|
+
if (!raw) return ["status"];
|
|
280
|
+
const lower = raw.toLowerCase();
|
|
281
|
+
if (/^(?:help|\?|도움|사용법)\b/i.test(raw)) return ["help"];
|
|
282
|
+
if (/(?:^|\s)(?:list|ls|sources?|status|show|상태|목록|리스트)(?:\s|$)/i.test(raw)) return ["list"];
|
|
283
|
+
if (/(?:^|\s)(?:open|inbox|finder|열어|열기|인박스)(?:\s|$)/i.test(raw)) return ["open"];
|
|
284
|
+
const tokens = shellSplitCli(raw);
|
|
285
|
+
const kind = inferOntologyKindCli(null, raw);
|
|
286
|
+
const scope = inferOntologyScopeCli(null, raw, kind);
|
|
287
|
+
let source = findOntologyPathTokenCli(tokens, cwd, raw);
|
|
288
|
+
if (!source && /(?:this folder|current folder|here|이\s*폴더|현재\s*폴더|지금\s*폴더|여기)/i.test(raw)) source = ".";
|
|
289
|
+
const wantsAdd = Boolean(source) || /(add|register|attach|source|watch|sync|추가|등록|붙|연결)/i.test(lower);
|
|
290
|
+
if (wantsAdd) {
|
|
291
|
+
if (!source) return ["add"];
|
|
292
|
+
return ["add", source, "--kind", kind, "--scope", scope];
|
|
293
|
+
}
|
|
294
|
+
if (/(enable|activate|start|turn on|켜|시작|활성)/i.test(lower)) return ["status"];
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function formatOntologyStatusCli(paths, lang) {
|
|
299
|
+
const ko = lang === "ko";
|
|
300
|
+
const sources = readOntologySourcesCli(paths.sourceManifestPath);
|
|
301
|
+
const inbox = listOntologyInboxCli(paths.inboxPath);
|
|
302
|
+
const lines = [
|
|
303
|
+
ko ? "온톨로지: 활성" : "Ontology: active",
|
|
304
|
+
` ${ko ? "프로젝트" : "project"}: ${paths.root}`,
|
|
305
|
+
` ${ko ? "수신함" : "inbox"}: ${paths.inboxPath}`,
|
|
306
|
+
` DB: ${paths.dbPath}`,
|
|
307
|
+
` ${ko ? "정책" : "policy"}: inbox_and_registered_sources_only`,
|
|
308
|
+
ko ? " 검색: 홈 폴더·형제 프로젝트 제외" : " scan: no home folder, no sibling projects",
|
|
309
|
+
"",
|
|
310
|
+
`${ko ? "수신함" : "Inbox"} (${inbox.length}):`,
|
|
311
|
+
];
|
|
312
|
+
for (const item of inbox) lines.push(` ${item.supported ? "✓" : "!"} ${item.name} ${item.supported ? (ko ? "지원됨" : "supported") : (ko ? "어댑터 대기" : "adapter pending")}`);
|
|
313
|
+
if (!inbox.length) lines.push(ko ? " (비어 있음)" : " (empty)");
|
|
314
|
+
lines.push("", `${ko ? "소스" : "Sources"} (${sources.length}):`);
|
|
315
|
+
for (const source of sources) {
|
|
316
|
+
const sourcePath = path.resolve(String(source.path || ""));
|
|
317
|
+
lines.push(` ${fs.existsSync(sourcePath) ? "✓" : "!"} ${sourcePath} ${source.kind || "project"} / ${source.scope || "internal"}`);
|
|
318
|
+
}
|
|
319
|
+
if (!sources.length) lines.push(ko ? " (없음)" : " (none)");
|
|
320
|
+
lines.push(
|
|
321
|
+
"",
|
|
322
|
+
ko ? "소스 추가:" : "Add sources:",
|
|
323
|
+
" /ontology add ./docs",
|
|
324
|
+
" /ontology company ./docs",
|
|
325
|
+
" /ontology personal ~/notes",
|
|
326
|
+
"",
|
|
327
|
+
ko ? "자연어 예시:" : "Natural examples:",
|
|
328
|
+
" /ontology use ./docs as company knowledge",
|
|
329
|
+
" /ontology attach ~/notes as personal private memory",
|
|
330
|
+
" /ontology open the inbox",
|
|
331
|
+
);
|
|
332
|
+
return lines;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function registerOntologySourceCli(paths, source, kind, scope, cwd, lang) {
|
|
336
|
+
const ko = lang === "ko";
|
|
337
|
+
if (!source) throw new Error(ko
|
|
338
|
+
? "사용법: /ontology add <경로> 또는 /ontology company ./docs"
|
|
339
|
+
: "usage: /ontology add <path> or /ontology company ./docs");
|
|
340
|
+
const sourcePath = resolveOntologyPathCli(source, cwd || paths.root);
|
|
341
|
+
if (!fs.existsSync(sourcePath)) throw new Error(ko ? `소스를 찾지 못했습니다: ${sourcePath}` : `source not found: ${sourcePath}`);
|
|
342
|
+
const manifest = readJsonSafeCli(paths.sourceManifestPath, ontologySourceManifestSkeletonCli(paths.root));
|
|
343
|
+
const nextSources = (Array.isArray(manifest.sources) ? manifest.sources : [])
|
|
344
|
+
.filter((item) => path.resolve(String(item.path || "")) !== sourcePath);
|
|
345
|
+
nextSources.push({ path: sourcePath, kind, scope, registeredAt: new Date().toISOString() });
|
|
346
|
+
manifest.schemaVersion = "1.0";
|
|
347
|
+
manifest.kind = "agentlas-ontology-source-manifest";
|
|
348
|
+
manifest.projectRoot = paths.root;
|
|
349
|
+
manifest.sources = nextSources;
|
|
350
|
+
writeJsonSafeCli(paths.sourceManifestPath, manifest);
|
|
351
|
+
return ko
|
|
352
|
+
? [
|
|
353
|
+
`온톨로지 소스 등록됨: ${sourcePath}`,
|
|
354
|
+
` 종류: ${kind}`,
|
|
355
|
+
` 범위: ${scope}`,
|
|
356
|
+
" 복사: 안 함",
|
|
357
|
+
" 스캔: 이 등록 폴더만 사용하며 홈/이웃 프로젝트는 스캔하지 않음",
|
|
358
|
+
]
|
|
359
|
+
: [
|
|
360
|
+
`Registered ontology source: ${sourcePath}`,
|
|
361
|
+
` kind: ${kind}`,
|
|
362
|
+
` scope: ${scope}`,
|
|
363
|
+
" copy: no",
|
|
364
|
+
" scan: only this registered folder, not home/sibling projects",
|
|
365
|
+
];
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function openLocalPathCli(targetPath, notify) {
|
|
369
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
|
|
370
|
+
try {
|
|
371
|
+
spawn(command, [targetPath], { detached: true, stdio: "ignore" }).unref();
|
|
372
|
+
} catch {
|
|
373
|
+
if (typeof notify === "function") notify(`Open manually: ${targetPath}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function runOntologyCli(args, opts) {
|
|
378
|
+
opts = opts || {};
|
|
379
|
+
const ko = opts.lang === "ko";
|
|
380
|
+
const cwd = path.resolve(opts.cwd || process.cwd());
|
|
381
|
+
const projectPath = path.resolve(opts.projectPath || cwd);
|
|
382
|
+
const normalizedArgs = Array.isArray(args) ? args : [];
|
|
383
|
+
const sub = normalizedArgs[0] || "status";
|
|
384
|
+
const passivePaths = ontologyPathsForCli(projectPath);
|
|
385
|
+
if (sub === "status" || sub === "list") {
|
|
386
|
+
if (!initializedAgentlasProjectPathCli(projectPath)) {
|
|
387
|
+
return [
|
|
388
|
+
ko ? "온톨로지: 초기화되지 않음" : "Ontology: not initialized",
|
|
389
|
+
` ${ko ? "프로젝트" : "project"}: ${projectPath}`,
|
|
390
|
+
ko ? " 생성된 파일 없음" : " no files were created",
|
|
391
|
+
ko ? " 명시적 초기화: agentlas project init" : " initialize explicitly: agentlas project init",
|
|
392
|
+
];
|
|
393
|
+
}
|
|
394
|
+
return formatOntologyStatusCli(passivePaths, opts.lang);
|
|
395
|
+
}
|
|
396
|
+
if (sub === "help" || sub === "--help" || sub === "-h") return ontologyUsageLinesCli(opts.lang);
|
|
397
|
+
const directOntologyCommand = ["open", "add", "company", "personal", "project"].includes(String(sub).toLowerCase())
|
|
398
|
+
|| isOntologyPathishCli(sub, cwd, true);
|
|
399
|
+
if (!directOntologyCommand) {
|
|
400
|
+
const parsed = parseOntologyNaturalArgsCli(normalizedArgs.join(" "), cwd);
|
|
401
|
+
if (!parsed) throw new Error(ko
|
|
402
|
+
? "사용법: /ontology status|list|open|add <경로>"
|
|
403
|
+
: "usage: /ontology status|list|open|add <path>");
|
|
404
|
+
return runOntologyCli(parsed, opts);
|
|
405
|
+
}
|
|
406
|
+
const paths = ensureOntologyCli(projectPath, opts.lang);
|
|
407
|
+
if (sub === "open") {
|
|
408
|
+
if (!opts.noOpen) openLocalPathCli(paths.inboxPath, opts.notify);
|
|
409
|
+
return [`${ko ? "온톨로지 수신함을 열었습니다" : "Opened ontology inbox"}: ${paths.inboxPath}`];
|
|
410
|
+
}
|
|
411
|
+
if (sub === "add") {
|
|
412
|
+
const flags = parseFlagsCli(normalizedArgs.slice(1));
|
|
413
|
+
const source = flags._[0];
|
|
414
|
+
const kind = inferOntologyKindCli(flags.kind || flags._[1], normalizedArgs.join(" "));
|
|
415
|
+
const scope = inferOntologyScopeCli(flags.scope || flags._[2], normalizedArgs.join(" "), kind);
|
|
416
|
+
return registerOntologySourceCli(paths, source, kind, scope, cwd, opts.lang);
|
|
417
|
+
}
|
|
418
|
+
if (["company", "personal", "project"].includes(String(sub).toLowerCase())) {
|
|
419
|
+
const flags = parseFlagsCli(normalizedArgs.slice(1));
|
|
420
|
+
const kind = inferOntologyKindCli(sub, normalizedArgs.join(" "));
|
|
421
|
+
const scope = inferOntologyScopeCli(flags.scope || flags._[1], normalizedArgs.join(" "), kind);
|
|
422
|
+
return registerOntologySourceCli(paths, flags._[0], kind, scope, cwd, opts.lang);
|
|
423
|
+
}
|
|
424
|
+
if (isOntologyPathishCli(sub, cwd, true)) {
|
|
425
|
+
return registerOntologySourceCli(paths, sub, inferOntologyKindCli(null, normalizedArgs.join(" ")), inferOntologyScopeCli(null, normalizedArgs.join(" "), "project"), cwd, opts.lang);
|
|
426
|
+
}
|
|
427
|
+
throw new Error(ko
|
|
428
|
+
? "사용법: /ontology status|list|open|add <경로>"
|
|
429
|
+
: "usage: /ontology status|list|open|add <path>");
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function runOntologyNaturalCli(text, opts) {
|
|
433
|
+
const cwd = path.resolve((opts && opts.cwd) || process.cwd());
|
|
434
|
+
const parsed = parseOntologyNaturalArgsCli(text, cwd);
|
|
435
|
+
if (!parsed) throw new Error((opts && opts.lang) === "ko"
|
|
436
|
+
? "사용법: /ontology status|list|open|add <경로>"
|
|
437
|
+
: "usage: /ontology status|list|open|add <path>");
|
|
438
|
+
return runOntologyCli(parsed, { ...(opts || {}), cwd });
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
module.exports = {
|
|
442
|
+
ONTOLOGY_SUPPORTED_EXTS,
|
|
443
|
+
parseFlagsCli,
|
|
444
|
+
ontologyPathsForCli,
|
|
445
|
+
readJsonSafeCli,
|
|
446
|
+
writeJsonSafeCli,
|
|
447
|
+
writeJsonPrivateAtomicCli,
|
|
448
|
+
ontologySourceManifestSkeletonCli,
|
|
449
|
+
ensureOntologyCli,
|
|
450
|
+
listOntologyInboxCli,
|
|
451
|
+
readOntologySourcesCli,
|
|
452
|
+
ontologyUsageLinesCli,
|
|
453
|
+
shellSplitCli,
|
|
454
|
+
expandUserPathCli,
|
|
455
|
+
cleanOntologyPathTokenCli,
|
|
456
|
+
resolveOntologyPathCli,
|
|
457
|
+
inferOntologyKindCli,
|
|
458
|
+
inferOntologyScopeCli,
|
|
459
|
+
isOntologyPathishCli,
|
|
460
|
+
findOntologyPathTokenCli,
|
|
461
|
+
parseOntologyNaturalArgsCli,
|
|
462
|
+
formatOntologyStatusCli,
|
|
463
|
+
registerOntologySourceCli,
|
|
464
|
+
openLocalPathCli,
|
|
465
|
+
runOntologyCli,
|
|
466
|
+
runOntologyNaturalCli,
|
|
467
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* project/paths — 프로젝트 경계 판정용 경로 헬퍼 (v1 runCwd/projectCwd 포팅).
|
|
4
|
+
*
|
|
5
|
+
* workforce/capture.cjs에도 같은 규칙의 projectCwd가 있지만 기능 디렉터리 간
|
|
6
|
+
* 수평 의존을 만들지 않기 위해 여기 별도 구현을 둔다. 규칙이 바뀌면 둘 다 함께
|
|
7
|
+
* 바꿔야 한다 (v1 monolith 9961–9981 라인이 원본 계약).
|
|
8
|
+
*/
|
|
9
|
+
const fs = require("node:fs");
|
|
10
|
+
const os = require("node:os");
|
|
11
|
+
const path = require("node:path");
|
|
12
|
+
const { userDataDir } = require("../core/paths.cjs");
|
|
13
|
+
|
|
14
|
+
/** 에이전트 격리 실행용 전용 폴더 — "프로젝트 아님" 위치의 안전한 폴백. */
|
|
15
|
+
function runCwd() {
|
|
16
|
+
const dir = path.join(userDataDir(), "agent-cwd");
|
|
17
|
+
try {
|
|
18
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
19
|
+
return dir;
|
|
20
|
+
} catch {
|
|
21
|
+
return os.homedir();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 에이전트가 실제로 실행될 작업 폴더 = 사용자가 명령을 친 현재 디렉터리(= 대상 프로젝트).
|
|
27
|
+
* 단, home/userData/agent-cwd 같은 "프로젝트 아님" 위치면 안전한 전용 폴더로 폴백한다.
|
|
28
|
+
*/
|
|
29
|
+
function projectCwd() {
|
|
30
|
+
try {
|
|
31
|
+
const cwd = process.cwd();
|
|
32
|
+
if (!cwd || cwd === os.homedir() || cwd === userDataDir() || cwd === runCwd()) return runCwd();
|
|
33
|
+
return cwd;
|
|
34
|
+
} catch {
|
|
35
|
+
return runCwd();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = { runCwd, projectCwd };
|