@myagentroam/node 0.9.62 → 0.9.65
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 +202 -0
- package/NOTICE +4 -0
- package/dist/capabilities.js +110 -2
- package/dist/claude-agent-sdk.js +330 -2
- package/dist/claude-channel.js +44 -2
- package/dist/codex-app-server.js +510 -2
- package/dist/config.js +106 -2
- package/dist/connector/node-connector-options.js +1 -2
- package/dist/connector/node-operation-router.js +22 -2
- package/dist/connector.js +1736 -2
- package/dist/control-outbound-scheduler.js +153 -2
- package/dist/database.js +639 -2
- package/dist/fake-runner.js +129 -2
- package/dist/git-askpass.js +12 -0
- package/dist/git-execution-helper.js +266 -0
- package/dist/health.js +3 -2
- package/dist/main.js +57 -2
- package/dist/mcp-installation-model.js +1 -2
- package/dist/migrations/v004.js +55 -3
- package/dist/migrations/v005.js +13 -3
- package/dist/migrations/v006.js +90 -3
- package/dist/migrations/v007.js +22 -3
- package/dist/migrations/v008.js +18 -3
- package/dist/migrations/v009.js +70 -4
- package/dist/migrations/v010.js +43 -3
- package/dist/migrations/v011.js +20 -3
- package/dist/migrations/v012.js +8 -0
- package/dist/native-jsonl-reader.js +186 -2
- package/dist/native-session-history.js +1143 -2
- package/dist/opencode-runtime.js +80 -2
- package/dist/opencode-server.js +259 -2
- package/dist/operational.js +122 -2
- package/dist/process-tree.js +18 -2
- package/dist/ripgrep-binary.js +31 -2
- package/dist/rotating-log.js +67 -2
- package/dist/runner/abstract-runner.js +260 -2
- package/dist/runner/claude/managed-run-controller.js +355 -2
- package/dist/runner/claude/tool-normalizer.js +120 -2
- package/dist/runner/claude-code-runner.js +303 -2
- package/dist/runner/codex/composer-image-markers.js +8 -2
- package/dist/runner/codex/conversation-parser.js +913 -2
- package/dist/runner/codex/managed-run-controller.js +494 -2
- package/dist/runner/codex/tool-normalizer.js +214 -2
- package/dist/runner/codex-runner.js +585 -2
- package/dist/runner/fake-test-runner.js +47 -2
- package/dist/runner/mar-agent/conversation-normalizer.js +644 -2
- package/dist/runner/mar-agent/executor-stdio-client-transport.js +139 -2
- package/dist/runner/mar-agent/generated-images.js +41 -2
- package/dist/runner/mar-agent/history-projection.js +106 -2
- package/dist/runner/mar-agent/hosted-conversation-runtime.js +446 -2
- package/dist/runner/mar-agent/managed-run-controller.js +785 -2
- package/dist/runner/mar-agent/mcp-client-pool.js +233 -2
- package/dist/runner/mar-agent/runtime-model-factory.js +326 -2
- package/dist/runner/mar-agent-runner.js +458 -2
- package/dist/runner/opencode/conversation-parser.js +221 -2
- package/dist/runner/opencode/managed-run-controller.js +588 -2
- package/dist/runner/opencode/tool-normalizer.js +80 -2
- package/dist/runner/opencode-runner.js +426 -2
- package/dist/runner/runner-registry.js +87 -2
- package/dist/runner-command-engine.js +71 -2
- package/dist/runner-profiles.js +323 -2
- package/dist/runner-usage.js +190 -2
- package/dist/runtime-command-detector.js +73 -2
- package/dist/runtime-model.js +1 -2
- package/dist/runtime-state.js +1509 -2
- package/dist/service/attachment-domain-state.js +7 -2
- package/dist/service/bubblewrap-sandbox-executor.js +543 -2
- package/dist/service/continuous-run-coordinator.js +425 -2
- package/dist/service/conversation-history-service.js +552 -2
- package/dist/service/conversation-hosting-capability-service.js +61 -2
- package/dist/service/conversation-hosting-enablement-service.js +91 -2
- package/dist/service/conversation-hosting-maintenance-service.js +174 -2
- package/dist/service/conversation-segment-service.js +162 -2
- package/dist/service/conversation-template-node-service.js +992 -2
- package/dist/service/external-session-resume-service.js +152 -2
- package/dist/service/fake-managed-run-service.js +60 -2
- package/dist/service/git-execution-service.js +418 -0
- package/dist/service/git-operation-service.js +57 -0
- package/dist/service/hosted-conversation-node-service.js +1982 -2
- package/dist/service/hosted-conversation-store.js +1405 -2
- package/dist/service/managed-runner-session-service.js +35 -2
- package/dist/service/mar-agent-model-transport-test-service.js +88 -2
- package/dist/service/mcp-installation-verifier.js +87 -2
- package/dist/service/mcp-node-operation-service.js +90 -2
- package/dist/service/mission-mcp-server.js +124 -2
- package/dist/service/mission-mcp-stdio.js +40 -2
- package/dist/service/mission-runtime.js +142 -2
- package/dist/service/native-session-projection-service.js +274 -2
- package/dist/service/native-session-watch-service.js +683 -2
- package/dist/service/node-connection-lifecycle-service.js +88 -2
- package/dist/service/node-control-channel.js +85 -2
- package/dist/service/node-control-message-service.js +109 -2
- package/dist/service/node-request-service.js +62 -2
- package/dist/service/node-run-event-bridge.js +88 -2
- package/dist/service/node-terminal-channel.js +202 -2
- package/dist/service/node-upgrade-coordinator.js +137 -2
- package/dist/service/node-workspace-coordinator.js +97 -2
- package/dist/service/recent-run-projection-service.js +99 -2
- package/dist/service/run-attachment-service.js +321 -2
- package/dist/service/run-domain-state.js +4 -2
- package/dist/service/run-event-service.js +246 -2
- package/dist/service/run-ownership-policy.js +22 -2
- package/dist/service/run-workbench-service.js +120 -2
- package/dist/service/runner-channel-message-service.js +20 -2
- package/dist/service/runner-interaction-service.js +243 -2
- package/dist/service/runner-service.js +69 -2
- package/dist/service/runtime-credential-control-service.js +95 -2
- package/dist/service/session-catalog-service.js +404 -2
- package/dist/service/session-command-service.js +125 -2
- package/dist/service/session-context-service.js +128 -2
- package/dist/service/session-domain-state.js +9 -2
- package/dist/service/session-identity-service.js +86 -2
- package/dist/service/session-lifecycle-service.js +248 -2
- package/dist/service/session-message-service.js +270 -2
- package/dist/service/session-presentation-service.js +43 -2
- package/dist/service/session-query-service.js +82 -2
- package/dist/service/session-watch-slot-coordinator.js +21 -2
- package/dist/service/skill-directory-service.js +238 -2
- package/dist/service/skill-install-service.js +434 -2
- package/dist/service/skill-node-operation-service.js +206 -2
- package/dist/service/terminal-relay-state.js +3 -2
- package/dist/service/terminal-service.js +90 -2
- package/dist/service/web-search-error.js +40 -2
- package/dist/service/workbench-event-service.js +21 -2
- package/dist/service/workbench-manifest-service.js +69 -2
- package/dist/service/workspace-change-service.js +290 -2
- package/dist/service/workspace-content-search-service.js +457 -2
- package/dist/service/workspace-domain-state.js +41 -2
- package/dist/service/workspace-file-service.js +150 -2
- package/dist/service/workspace-git-exclude-service.js +119 -2
- package/dist/service/workspace-queue-workbench-service.js +176 -2
- package/dist/service/workspace-service.js +92 -2
- package/dist/service/workspace-session-service.js +154 -2
- package/dist/service/workspace-upload-service.js +262 -2
- package/dist/service/workspace-watch-controller.js +68 -2
- package/dist/service/workspace-watch-service.js +170 -2
- package/dist/service/workspace-watch-snapshot-service.js +218 -2
- package/dist/service/workspace-workbench-service.js +239 -2
- package/dist/service.js +91 -2
- package/dist/storage.js +32 -2
- package/dist/supervisor.js +136 -2
- package/dist/terminal.js +496 -2
- package/dist/util/git-credential-client.js +32 -0
- package/dist/util/node-operation-data.js +9 -2
- package/dist/util/node-operation-parsers.js +382 -2
- package/dist/util/personal-instructions.js +34 -2
- package/dist/util/runner-error.js +70 -2
- package/dist/util/runner-native-session-parsers.js +510 -2
- package/dist/util/safe-error.js +8 -2
- package/dist/util/secret-environment.js +25 -2
- package/dist/workspace.js +1793 -2
- package/package.json +11 -8
- package/vendor/ripgrep/darwin-arm64/rg +0 -0
- package/vendor/ripgrep/darwin-x64/rg +0 -0
- package/vendor/ripgrep/manifest.json +12 -0
- package/README.md +0 -42
- package/dist/event-buffer.js +0 -2
- package/dist/migrations/v001.js +0 -107
- package/dist/migrations/v002.js +0 -2
- package/dist/migrations/v003.js +0 -2
- package/dist/service/mcp-package-installer.js +0 -2
|
@@ -1,2 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export class SessionPresentationService {
|
|
2
|
+
options;
|
|
3
|
+
constructor(options) {
|
|
4
|
+
this.options = options;
|
|
5
|
+
}
|
|
6
|
+
present(session) {
|
|
7
|
+
const database = this.options.database();
|
|
8
|
+
if (session.externalSessionId !== null && database !== undefined) {
|
|
9
|
+
const metadata = database.getNativeSessionMetadata(session.runner, session.externalSessionId, session.workspaceId);
|
|
10
|
+
if (metadata !== undefined)
|
|
11
|
+
session = { ...session, pinnedAt: metadata.pinnedAt, pinOrder: metadata.pinOrder };
|
|
12
|
+
}
|
|
13
|
+
const presentation = this.options.runners
|
|
14
|
+
.require(session.runner)
|
|
15
|
+
.presentSession(session, this.options.capabilities(), {
|
|
16
|
+
managedActive: this.options.hasManagedActiveRun(session),
|
|
17
|
+
externalActivity: this.options.externalActivity(session.id),
|
|
18
|
+
commandStates: this.options.commands.list(session.id)
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
...session,
|
|
22
|
+
...presentation
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
presentDiscovered(sessions) {
|
|
26
|
+
return sessions.map((session) => {
|
|
27
|
+
const projected = this.options.projectionForNative(session);
|
|
28
|
+
if (projected === undefined)
|
|
29
|
+
return this.present(session);
|
|
30
|
+
const synchronized = session.runnerTitle === null
|
|
31
|
+
? projected
|
|
32
|
+
: this.options.runtime.setRunnerTitleFromNative(projected.id, session.runnerTitle);
|
|
33
|
+
return this.present({
|
|
34
|
+
...synchronized,
|
|
35
|
+
lastActivityAt: latestSessionActivityAt(synchronized.lastActivityAt, session.lastActivityAt)
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function latestSessionActivityAt(current, discovered) {
|
|
41
|
+
const known = [current, discovered].filter((value) => Number.isFinite(value) && value > 0);
|
|
42
|
+
return known.length === 0 ? 0 : Math.max(...known);
|
|
43
|
+
}
|
|
@@ -1,2 +1,82 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { isPlainRecord } from '../runner/codex/conversation-parser.js';
|
|
2
|
+
export class SessionQueryService {
|
|
3
|
+
options;
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.options = options;
|
|
6
|
+
}
|
|
7
|
+
operations() {
|
|
8
|
+
return {
|
|
9
|
+
'session.get': (data) => this.get(record(data)),
|
|
10
|
+
'session.runs': (data) => this.runs(record(data)),
|
|
11
|
+
'session.turns.list': (data) => this.turns(record(data)),
|
|
12
|
+
'session.events.read': (data) => this.events(record(data)),
|
|
13
|
+
'session.context.read': (data) => this.readContext(record(data)),
|
|
14
|
+
'session.discover': (data) => this.discover(record(data))
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async get(input) {
|
|
18
|
+
return { session: this.options.present(await this.requireSession(input.sessionId)) };
|
|
19
|
+
}
|
|
20
|
+
async runs(input) {
|
|
21
|
+
const session = await this.requireSession(input.sessionId);
|
|
22
|
+
return { runs: this.options.runtime.listRunsForSession(session.id) };
|
|
23
|
+
}
|
|
24
|
+
async turns(input) {
|
|
25
|
+
const page = pageInput(input);
|
|
26
|
+
const session = await this.requireSession(input.sessionId);
|
|
27
|
+
if (input.unit === 'SEGMENT')
|
|
28
|
+
return this.options.segments(session, page);
|
|
29
|
+
if (input.unit !== undefined && input.unit !== 'TURN')
|
|
30
|
+
throw new Error('EVENT_CURSOR_INVALID');
|
|
31
|
+
return this.options.history(session, page);
|
|
32
|
+
}
|
|
33
|
+
events(input) {
|
|
34
|
+
const page = pageInput(input);
|
|
35
|
+
if (typeof input.sessionId !== 'string')
|
|
36
|
+
throw new Error('SESSION_NOT_FOUND');
|
|
37
|
+
const session = this.options.runtime.getAgentSession(input.sessionId);
|
|
38
|
+
if (session === undefined)
|
|
39
|
+
throw new Error('SESSION_NOT_FOUND');
|
|
40
|
+
return this.options.runtime.listConversationItems({ sessionId: session.id, ...page });
|
|
41
|
+
}
|
|
42
|
+
async readContext(input) {
|
|
43
|
+
return this.options.context.read(await this.requireSession(input.sessionId));
|
|
44
|
+
}
|
|
45
|
+
async discover(input) {
|
|
46
|
+
const runner = this.options.runners.product(input.runner);
|
|
47
|
+
if (runner === undefined)
|
|
48
|
+
throw new Error('SESSION_INVALID');
|
|
49
|
+
const workspaceId = typeof input.workspaceId === 'string'
|
|
50
|
+
? input.workspaceId
|
|
51
|
+
: typeof input.cwd === 'string'
|
|
52
|
+
? this.options.workspaceIdForCwd(input.cwd)
|
|
53
|
+
: undefined;
|
|
54
|
+
if (runner.discoveryScope === 'WORKSPACE_REQUIRED' && workspaceId === undefined)
|
|
55
|
+
throw new Error('SESSION_INVALID');
|
|
56
|
+
const sessions = await this.options.discover(runner.name, workspaceId);
|
|
57
|
+
return { sessions: sessions.map((session) => this.options.present(session)) };
|
|
58
|
+
}
|
|
59
|
+
async requireSession(sessionId) {
|
|
60
|
+
if (typeof sessionId !== 'string')
|
|
61
|
+
throw new Error('SESSION_INVALID');
|
|
62
|
+
const session = this.options.runtime.getAgentSession(sessionId) ?? (await this.options.resolve(sessionId));
|
|
63
|
+
if (session === undefined)
|
|
64
|
+
throw new Error('SESSION_NOT_FOUND');
|
|
65
|
+
return session;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function pageInput(input) {
|
|
69
|
+
if ((input.cursor !== undefined && typeof input.cursor !== 'string') ||
|
|
70
|
+
(input.limit !== undefined &&
|
|
71
|
+
(!Number.isInteger(input.limit) ||
|
|
72
|
+
input.limit < 1 ||
|
|
73
|
+
input.limit > 500)))
|
|
74
|
+
throw new Error('EVENT_CURSOR_INVALID');
|
|
75
|
+
return {
|
|
76
|
+
...(typeof input.cursor === 'string' ? { cursor: input.cursor } : {}),
|
|
77
|
+
...(typeof input.limit === 'number' ? { limit: input.limit } : {})
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function record(value) {
|
|
81
|
+
return isPlainRecord(value) ? value : {};
|
|
82
|
+
}
|
|
@@ -1,2 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/** Coordinates the single Session Watch slot owned by one logical Workbench connection. */
|
|
2
|
+
export class SessionWatchSlotCoordinator {
|
|
3
|
+
slots = new Map();
|
|
4
|
+
replace(ownerKey, kind, release) {
|
|
5
|
+
const previous = this.slots.get(ownerKey);
|
|
6
|
+
if (previous !== undefined) {
|
|
7
|
+
this.slots.delete(ownerKey);
|
|
8
|
+
previous.release();
|
|
9
|
+
}
|
|
10
|
+
const token = Symbol(kind);
|
|
11
|
+
this.slots.set(ownerKey, { kind, token, release });
|
|
12
|
+
return token;
|
|
13
|
+
}
|
|
14
|
+
owns(ownerKey, token) {
|
|
15
|
+
return this.slots.get(ownerKey)?.token === token;
|
|
16
|
+
}
|
|
17
|
+
release(ownerKey, token) {
|
|
18
|
+
if (this.slots.get(ownerKey)?.token === token)
|
|
19
|
+
this.slots.delete(ownerKey);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -1,2 +1,238 @@
|
|
|
1
|
-
|
|
2
|
-
const _0x408b4e=_0x2869;(function(_0x191f78,_0x49db89){const _0x18c50a=_0x2869,_0x95f7e2=_0x191f78();while(!![]){try{const _0x3ab2ae=-parseInt(_0x18c50a(0xe1))/(-0x1736*-0x1+0x238*0x9+0x62b*-0x7)+-parseInt(_0x18c50a(0xa5))/(0x4*-0xee+-0x7*-0x85+0x17*0x1)*(parseInt(_0x18c50a(0xa4))/(0xf3a+-0x1*-0xaa2+-0x19d9))+parseInt(_0x18c50a(0xe2))/(-0x1*-0x775+0x1ac7+-0x2238)+-parseInt(_0x18c50a(0x9f))/(0x29*0xbc+0xeff+-0x3a*0xc7)*(-parseInt(_0x18c50a(0xb7))/(0x16bf+0xc4d+-0x2306))+parseInt(_0x18c50a(0xbe))/(-0x1*-0x11f2+0x37*-0xb+-0xb5*0x16)+parseInt(_0x18c50a(0xdc))/(0x6dc*-0x2+-0x5*-0x1a1+0x59b)*(-parseInt(_0x18c50a(0xb3))/(-0x1558+-0x1d*0x151+0x882*0x7))+parseInt(_0x18c50a(0xa9))/(-0x12a*-0x1c+0x44f*0x2+-0x292c);if(_0x3ab2ae===_0x49db89)break;else _0x95f7e2['push'](_0x95f7e2['shift']());}catch(_0x11ba06){_0x95f7e2['push'](_0x95f7e2['shift']());}}}(_0x3c6d,-0x1*-0x65b28+-0x2e01*-0x1a+-0x4807d));import{lstat,open,readdir,readlink,realpath}from'node:fs/promises';import _0x2bfd99 from'node:path';import{skillInstallMetadataSchema}from'@myagentroam/protocol';import{hasBrokenWorkspaceSkillGitExclude}from'./workspace-git-exclude-service.js';const MAX_METADATA_BYTES=(0x8ce+-0x866+0x5*-0x8)*(0x14f8+-0x1171+-0xb*-0xb),SKILL_NAME=/^[a-z0-9]+(?:-[a-z0-9]+)*$/u,WINDOWS_DEVICE_NAME=/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])$/u;function _0x3c6d(){const _0x2ef9be=['r0Lux0vyq0XvrevFqLjps0vo','zxHLyW','BMfTzq','CMvHza','mtqWmtKZmNvqCvvvBa','AxneAxjLy3rVCNK','DxrMoa','C3rHCNrZv2L0Aa','CgfYC2u','lM1HCI1ZA2LSBc1PBNn0ywXSlMPZB24','C3bSAxq','u0TjteXFtuvuqurbvefFsu5wquXjra','lMfNzw50CW','AM9PBG','C2TPBgXZ','vu5bvKfjtefcteu','su5wquXjrf9msu5l','q0Xbvurf','BwfW','zw50CMLLCW','v09ss1nqqunf','ywXSB2m','Aw5ZDgfSBa','tufoquDfrf9dt1bz','lMnSyxvKzq','Aw5ZCgvJDfDVCMTZCgfJzq','C3rHDa','CgXHDgzVCM0','C29YDa','C2TPBgXgAwXLugf0Aa','y2XHDwrL','zgLYBMfTzq','lM1HCI0','CMvZB2X2zq','oenPExrYsq','Aw5JBhvKzunSyxvKzunVBxbHDgLIAwXPDhK','u0TjteWUBwq','Bg9JywXtDgf0Dxm','tK9erq','odmYnZiYseXHAvzi','ntK4nZG0AgLeCNLn','Bg9JywXLq29TCgfYzq','vKfmsuq','C2v0','vu5ltK9xtL9wrvjtsu9o','Aw5ZCgvJDe5VzgviB21L','DgvZDa','ndqZmergwM1psa','y29Kzq','Dg9tDhjPBMC','BgvUz3rO','D2LUmZi','mJa3nJm5BM9iBKXg','mtHVtvHoqNe','u1LnteLosW','AxngAwXL','uefsveLbta','mJe4ndC4otbIt2TNAM8','C3rYAw5NAwz5','su5wquXjra','C2XPy2u','zgvZy3jPChrPB24','ywDLBNrZ','z2v0','quDftLrt','sLvoq1rjt04','ChvZAa','nJGZntq3m0TWvfLTwq','C2L6zq','AxntEw1IB2XPy0XPBMS','y2XVC2u','nZmYwurKCufX','zw5KC1DPDgG','ru5pru5u'];_0x3c6d=function(){return _0x2ef9be;};return _0x3c6d();}export class SkillDirectoryService{async[_0x408b4e(0x9d)](_0x5b5a22,_0x10880e={}){const _0x5180f6=_0x408b4e,_0x554e49=_0x2bfd99[_0x5180f6(0xc7)](_0x5b5a22,_0x5180f6(0xc6),_0x5180f6(0xc8)),_0x41c724=_0x2bfd99[_0x5180f6(0xc7)](_0x5b5a22,_0x5180f6(0xd2),_0x5180f6(0xc8)),_0xd9e509=await nodeCompatibilityMode(_0x554e49,_0x41c724),_0x1d683e=await scanRoot(_0x554e49,_0x5180f6(0xb0)),_0x21fff4=_0x10880e[_0x5180f6(0xdd)]===!![]&&_0xd9e509!==_0x5180f6(0xc9)?await scanRoot(_0x41c724,_0x5180f6(0xcb)):[];return{'targetKind':_0x5180f6(0xe0),'compatibilityMode':_0xd9e509,'skills':_0x10880e[_0x5180f6(0xdd)]===!![]?mergeNodeCompatibilityRoots(_0x1d683e,_0x21fff4):_0x1d683e[_0x5180f6(0xcc)](withoutSource)};}async[_0x408b4e(0xd3)](_0x41f71c){const _0x4fd9bc=_0x408b4e,_0x53c991=await scanRoot(_0x2bfd99[_0x4fd9bc(0xc7)](_0x41f71c,_0x4fd9bc(0xc6),_0x4fd9bc(0xc8)),_0x4fd9bc(0xb0)),_0x32087f=await scanRoot(_0x2bfd99[_0x4fd9bc(0xc7)](_0x41f71c,_0x4fd9bc(0xd2),_0x4fd9bc(0xc8)),_0x4fd9bc(0xcb)),_0x2a6000=mergeWorkspaceCopies(_0x53c991,_0x32087f),_0x17b842=await hasBrokenWorkspaceSkillGitExclude(_0x41f71c,_0x2a6000[_0x4fd9bc(0xcc)](_0x5c3606=>_0x5c3606[_0x4fd9bc(0xbc)]));return{'targetKind':_0x4fd9bc(0xce),'compatibilityMode':_0x4fd9bc(0xd1),'skills':_0x17b842?_0x2a6000[_0x4fd9bc(0xcc)](_0xa55a06=>({..._0xa55a06,'localStatus':_0x4fd9bc(0xba)})):_0x2a6000};}}async function scanRoot(_0x2891e9,_0x41bd6d){const _0x5207eb=_0x408b4e;let _0x4c0f7e;try{_0x4c0f7e=await readdir(_0x2891e9,{'withFileTypes':!![]});}catch(_0x3c3ef9){if(isMissing(_0x3c3ef9))return[];throw _0x3c3ef9;}const _0x55e3f7=[];for(const _0x53606a of _0x4c0f7e[_0x5207eb(0xd6)]((_0x5de919,_0x2eeb3b)=>_0x5de919[_0x5207eb(0xbc)][_0x5207eb(0xe3)](_0x2eeb3b[_0x5207eb(0xbc)]))){if(_0x53606a[_0x5207eb(0xbc)][_0x5207eb(0xc1)](_0x5207eb(0xda)))continue;const _0x486a66=_0x2bfd99[_0x5207eb(0xc7)](_0x2891e9,_0x53606a[_0x5207eb(0xbc)]),_0x32b96b=await lstat(_0x486a66);if(_0x32b96b[_0x5207eb(0xb5)]()||!_0x32b96b[_0x5207eb(0xbf)]()&&process[_0x5207eb(0xd5)]===_0x5207eb(0xa3)){_0x55e3f7[_0x5207eb(0xb2)]({'name':_0x53606a[_0x5207eb(0xbc)],'description':'','localStatus':_0x5207eb(0xca),'skillFilePath':_0x2bfd99[_0x5207eb(0xc7)](_0x486a66,_0x5207eb(0xde)),'source':_0x41bd6d});continue;}if(!_0x32b96b[_0x5207eb(0xbf)]())continue;if(!SKILL_NAME[_0x5207eb(0x9e)](_0x53606a[_0x5207eb(0xbc)])||WINDOWS_DEVICE_NAME[_0x5207eb(0x9e)](_0x53606a[_0x5207eb(0xbc)])){_0x55e3f7[_0x5207eb(0xb2)]({'name':_0x53606a[_0x5207eb(0xbc)],'description':'','localStatus':_0x5207eb(0xab),'skillFilePath':_0x2bfd99[_0x5207eb(0xc7)](_0x486a66,_0x5207eb(0xde)),'source':_0x41bd6d});continue;}_0x55e3f7[_0x5207eb(0xb2)]({...await inspectSkill(_0x486a66,_0x53606a[_0x5207eb(0xbc)]),'source':_0x41bd6d});}return _0x55e3f7;}async function inspectSkill(_0x20addf,_0x47ac2a){const _0x40c980=_0x408b4e;try{const _0x180af6=parseSkillFrontmatter(await readBounded(_0x2bfd99[_0x40c980(0xc7)](_0x20addf,_0x40c980(0xde))));if(_0x180af6[_0x40c980(0xbc)]!==_0x47ac2a||!SKILL_NAME[_0x40c980(0x9e)](_0x180af6[_0x40c980(0xbc)])||WINDOWS_DEVICE_NAME[_0x40c980(0x9e)](_0x180af6[_0x40c980(0xbc)]))return{'name':_0x47ac2a,'description':_0x180af6[_0x40c980(0xad)],'localStatus':_0x40c980(0xab),'skillFilePath':_0x2bfd99[_0x40c980(0xc7)](_0x20addf,_0x40c980(0xde))};const _0x4799c5=await readInstallMetadata(_0x2bfd99[_0x40c980(0xc7)](_0x20addf,_0x40c980(0xc3)));return{'name':_0x47ac2a,'description':_0x180af6[_0x40c980(0xad)],'localStatus':_0x4799c5===undefined?_0x40c980(0xe6):_0x40c980(0xe4),'skillFilePath':_0x2bfd99[_0x40c980(0xc7)](_0x20addf,_0x40c980(0xde)),..._0x4799c5===undefined?{}:{'install':_0x4799c5}};}catch{return{'name':_0x47ac2a,'description':'','localStatus':_0x40c980(0xab),'skillFilePath':_0x2bfd99[_0x40c980(0xc7)](_0x20addf,_0x40c980(0xde))};}}async function readInstallMetadata(_0xcfd9ed){const _0x1a5591=_0x408b4e;try{return skillInstallMetadataSchema[_0x1a5591(0xc2)](JSON[_0x1a5591(0xc2)](await readBounded(_0xcfd9ed)));}catch(_0x541002){if(isMissing(_0x541002))return undefined;return undefined;}}async function readBounded(_0x1d9b4a){const _0xc3a481=_0x408b4e,_0x386471=await open(_0x1d9b4a,'r');try{const _0x294289=await _0x386471[_0xc3a481(0xd4)]();if(!_0x294289[_0xc3a481(0xa7)]()||_0x294289[_0xc3a481(0xb4)]>MAX_METADATA_BYTES)throw new Error(_0xc3a481(0xc5));const _0x1be057=Buffer[_0xc3a481(0xcf)](_0x294289[_0xc3a481(0xb4)]);return await _0x386471[_0xc3a481(0xbd)](_0x1be057,-0xb94+0x108a+-0x4f6,_0x1be057[_0xc3a481(0xa2)],-0x65*-0x2c+-0x1*-0xb7e+-0x1cda),_0x1be057[_0xc3a481(0xa1)](_0xc3a481(0xc0));}finally{await _0x386471[_0xc3a481(0xb6)]();}}function parseSkillFrontmatter(_0x3debfe){const _0x353bea=_0x408b4e,_0x3cb7ba=/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u[_0x353bea(0xbb)](_0x3debfe);if(_0x3cb7ba===null)throw new Error(_0x353bea(0xc5));const _0x4d046f=new Map();for(const _0x362114 of _0x3cb7ba[0x16ed*-0x1+0x2*0x1276+-0xdfe][_0x353bea(0xc4)](/\r?\n/u)){const _0x4b5b36=/^(name|description):\s*(.*?)\s*$/u[_0x353bea(0xbb)](_0x362114);if(_0x4b5b36!==null)_0x4d046f[_0x353bea(0xe5)](_0x4b5b36[-0x7*-0xcd+0x91+-0x62b],unquote(_0x4b5b36[0x62c*-0x6+0x1*0x2417+0xf3]));}const _0x286da9=_0x4d046f[_0x353bea(0xaf)](_0x353bea(0xbc)),_0x15d42d=_0x4d046f[_0x353bea(0xaf)](_0x353bea(0xad));if(_0x286da9===undefined||_0x15d42d===undefined||_0x15d42d[_0x353bea(0xa2)]>0x12+0x652+0x99c)throw new Error(_0x353bea(0xc5));return{'name':_0x286da9,'description':_0x15d42d};}function unquote(_0x166421){const _0x270be5=_0x408b4e;if(_0x166421[_0x270be5(0xa2)]>=-0x2550+-0xaff+0x3051&&(_0x166421[_0x270be5(0xc1)]('\x22')&&_0x166421[_0x270be5(0xb8)]('\x22')||_0x166421[_0x270be5(0xc1)]('\x27')&&_0x166421[_0x270be5(0xb8)]('\x27')))return _0x166421[_0x270be5(0xac)](0x167d+-0x25df+-0x521*-0x3,-(-0x1f44+-0x2*-0x85b+0x1*0xe8f));return _0x166421;}function mergeWorkspaceCopies(_0x573db1,_0x3f329e){const _0x18b6b9=_0x408b4e,_0x2ec73b=new Map();for(const _0x49c3b1 of _0x573db1)_0x2ec73b[_0x18b6b9(0xe5)](_0x49c3b1[_0x18b6b9(0xbc)],{'agents':_0x49c3b1});for(const _0x22c34f of _0x3f329e)_0x2ec73b[_0x18b6b9(0xe5)](_0x22c34f[_0x18b6b9(0xbc)],{..._0x2ec73b[_0x18b6b9(0xaf)](_0x22c34f[_0x18b6b9(0xbc)]),'claude':_0x22c34f});return[..._0x2ec73b[_0x18b6b9(0xcd)]()][_0x18b6b9(0xd6)](([_0x41f4e3],[_0x11a578])=>_0x41f4e3[_0x18b6b9(0xe3)](_0x11a578))[_0x18b6b9(0xcc)](([_0x962b94,_0x5895b2])=>{const _0x3a6576=_0x18b6b9;if(_0x5895b2[_0x3a6576(0xae)]===undefined||_0x5895b2[_0x3a6576(0xd8)]===undefined)return{...withoutSource(_0x5895b2[_0x3a6576(0xae)]??_0x5895b2[_0x3a6576(0xd8)]),'name':_0x962b94,'localStatus':_0x3a6576(0xa8)};if(_0x5895b2[_0x3a6576(0xae)][_0x3a6576(0xdf)]!==_0x5895b2[_0x3a6576(0xd8)][_0x3a6576(0xdf)]||JSON[_0x3a6576(0xaa)](_0x5895b2[_0x3a6576(0xae)][_0x3a6576(0xd0)])!==JSON[_0x3a6576(0xaa)](_0x5895b2[_0x3a6576(0xd8)][_0x3a6576(0xd0)]))return{'name':_0x962b94,'description':_0x5895b2[_0x3a6576(0xae)][_0x3a6576(0xad)],'localStatus':_0x3a6576(0xa8),'skillFilePath':_0x5895b2[_0x3a6576(0xae)][_0x3a6576(0xd7)]};return withoutSource(_0x5895b2[_0x3a6576(0xae)]);});}function _0x2869(_0x5c3606,_0xa55a06){_0x5c3606=_0x5c3606-(-0x1*0x218b+-0x1561+0x3789);const _0x2891e9=_0x3c6d();let _0x41bd6d=_0x2891e9[_0x5c3606];if(_0x2869['SgXvtG']===undefined){var _0x4c0f7e=function(_0x53606a){const _0x5de919='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2eeb3b='',_0x486a66='';for(let _0x32b96b=0x2159*0x1+-0x4*-0x229+-0x29fd,_0x20addf,_0x47ac2a,_0x180af6=0x71*-0x39+0xce4+0xc45;_0x47ac2a=_0x53606a['charAt'](_0x180af6++);~_0x47ac2a&&(_0x20addf=_0x32b96b%(-0x1cb5+0xa4c+-0x1*-0x126d)?_0x20addf*(0x17*-0x3a+-0x1c1*0x7+-0x1*-0x11bd)+_0x47ac2a:_0x47ac2a,_0x32b96b++%(0x148f*-0x1+0x1*-0x16ed+-0x40*-0xae))?_0x2eeb3b+=String['fromCharCode'](-0x7*-0xcd+0x91+-0x52d&_0x20addf>>(-(0x62c*-0x6+0x1*0x2417+0xf3)*_0x32b96b&0x12+0x652+-0x65e)):-0x2550+-0xaff+0x304f){_0x47ac2a=_0x5de919['indexOf'](_0x47ac2a);}for(let _0x4799c5=0x167d+-0x25df+-0x7b1*-0x2,_0xcfd9ed=_0x2eeb3b['length'];_0x4799c5<_0xcfd9ed;_0x4799c5++){_0x486a66+='%'+('00'+_0x2eeb3b['charCodeAt'](_0x4799c5)['toString'](-0x1f44+-0x2*-0x85b+0x2*0x74f))['slice'](-(-0x308+0x23c6+-0x68c*0x5));}return decodeURIComponent(_0x486a66);};_0x2869['IvUYOO']=_0x4c0f7e,_0x2869['pNkDmG']={},_0x2869['SgXvtG']=!![];}const _0x55e3f7=_0x2891e9[0x27f*-0x6+-0x3*0x3bd+0x1a31];_0x2869['RtkDol']!==_0x55e3f7&&(_0x2869['pNkDmG']={},_0x2869['RtkDol']=_0x55e3f7);const _0x3c3ef9=_0x2869['pNkDmG'][_0x5c3606];return _0x3c3ef9===undefined?(_0x41bd6d=_0x2869['IvUYOO'](_0x41bd6d),_0x2869['pNkDmG'][_0x5c3606]=_0x41bd6d):_0x41bd6d=_0x3c3ef9,_0x41bd6d;}function mergeNodeCompatibilityRoots(_0x555fbf,_0x15e5d5){const _0x109c3d=_0x408b4e,_0x53cb2a=new Map();for(const _0x1b97c9 of _0x555fbf)_0x53cb2a[_0x109c3d(0xe5)](_0x1b97c9[_0x109c3d(0xbc)],{'agents':_0x1b97c9});for(const _0x5cb5bd of _0x15e5d5)_0x53cb2a[_0x109c3d(0xe5)](_0x5cb5bd[_0x109c3d(0xbc)],{..._0x53cb2a[_0x109c3d(0xaf)](_0x5cb5bd[_0x109c3d(0xbc)]),'claude':_0x5cb5bd});return[..._0x53cb2a[_0x109c3d(0xcd)]()][_0x109c3d(0xd6)](([_0x39df2d],[_0x1c8db7])=>_0x39df2d[_0x109c3d(0xe3)](_0x1c8db7))[_0x109c3d(0xcc)](([_0x532c01,_0x4ef460])=>{const _0x25b02d=_0x109c3d;if(_0x4ef460[_0x25b02d(0xae)]===undefined||_0x4ef460[_0x25b02d(0xd8)]===undefined)return withoutSource(_0x4ef460[_0x25b02d(0xae)]??_0x4ef460[_0x25b02d(0xd8)]);if(_0x4ef460[_0x25b02d(0xae)][_0x25b02d(0xad)]!==_0x4ef460[_0x25b02d(0xd8)][_0x25b02d(0xad)]||_0x4ef460[_0x25b02d(0xae)][_0x25b02d(0xdf)]!==_0x4ef460[_0x25b02d(0xd8)][_0x25b02d(0xdf)]||JSON[_0x25b02d(0xaa)](_0x4ef460[_0x25b02d(0xae)][_0x25b02d(0xd0)])!==JSON[_0x25b02d(0xaa)](_0x4ef460[_0x25b02d(0xd8)][_0x25b02d(0xd0)]))return{'name':_0x532c01,'description':_0x4ef460[_0x25b02d(0xae)][_0x25b02d(0xad)],'localStatus':_0x25b02d(0xa8),'skillFilePath':_0x4ef460[_0x25b02d(0xae)][_0x25b02d(0xd7)]};return withoutSource(_0x4ef460[_0x25b02d(0xae)]);});}function withoutSource(_0x3457ec){const _0x3342ac=_0x408b4e;return{'name':_0x3457ec[_0x3342ac(0xbc)],'description':_0x3457ec[_0x3342ac(0xad)],'localStatus':_0x3457ec[_0x3342ac(0xdf)],'skillFilePath':_0x3457ec[_0x3342ac(0xd7)],..._0x3457ec[_0x3342ac(0xd0)]===undefined?{}:{'install':_0x3457ec[_0x3342ac(0xd0)]}};}async function nodeCompatibilityMode(_0x130055,_0x300a95){const _0x545c7d=_0x408b4e;try{const _0x3686e9=await lstat(_0x300a95);if(_0x3686e9[_0x545c7d(0xb5)]()){const _0x2344b1=await realpath(_0x2bfd99[_0x545c7d(0xdb)](_0x2bfd99[_0x545c7d(0xd9)](_0x300a95),await readlink(_0x300a95))),_0x270042=await realpath(_0x130055);return _0x2344b1===_0x270042?process[_0x545c7d(0xd5)]===_0x545c7d(0xa3)?_0x545c7d(0xb1):_0x545c7d(0xa6):_0x545c7d(0xc9);}return _0x3686e9[_0x545c7d(0xbf)]()?_0x545c7d(0xd1):_0x545c7d(0xc9);}catch(_0x35493e){if(isMissing(_0x35493e))return _0x545c7d(0xc9);return _0x545c7d(0xc9);}}function isMissing(_0x903963){const _0x1fe2c6=_0x408b4e;return _0x903963 instanceof Error&&_0x1fe2c6(0xa0)in _0x903963&&_0x903963[_0x1fe2c6(0xa0)]===_0x1fe2c6(0xb9);}
|
|
1
|
+
import { lstat, open, readdir, readlink, realpath } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { skillInstallMetadataSchema } from '@myagentroam/protocol';
|
|
4
|
+
import { hasBrokenWorkspaceSkillGitExclude } from './workspace-git-exclude-service.js';
|
|
5
|
+
const MAX_METADATA_BYTES = 64 * 1024;
|
|
6
|
+
const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
7
|
+
const WINDOWS_DEVICE_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])$/u;
|
|
8
|
+
export class SkillDirectoryService {
|
|
9
|
+
async inspectNodeHome(home, options = {}) {
|
|
10
|
+
const agentsRoot = path.join(home, '.agents', 'skills');
|
|
11
|
+
const claudeRoot = path.join(home, '.claude', 'skills');
|
|
12
|
+
const compatibilityMode = await nodeCompatibilityMode(agentsRoot, claudeRoot);
|
|
13
|
+
const agents = await scanRoot(agentsRoot, 'AGENTS');
|
|
14
|
+
const claude = options.includeClaudeCompatibility === true && compatibilityMode !== 'UNAVAILABLE'
|
|
15
|
+
? await scanRoot(claudeRoot, 'CLAUDE')
|
|
16
|
+
: [];
|
|
17
|
+
return {
|
|
18
|
+
targetKind: 'NODE',
|
|
19
|
+
compatibilityMode,
|
|
20
|
+
skills: options.includeClaudeCompatibility === true
|
|
21
|
+
? mergeNodeCompatibilityRoots(agents, claude)
|
|
22
|
+
: agents.map(withoutSource)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async inspectWorkspace(workspace) {
|
|
26
|
+
const agents = await scanRoot(path.join(workspace, '.agents', 'skills'), 'AGENTS');
|
|
27
|
+
const claude = await scanRoot(path.join(workspace, '.claude', 'skills'), 'CLAUDE');
|
|
28
|
+
const skills = mergeWorkspaceCopies(agents, claude);
|
|
29
|
+
const gitExcludeBroken = await hasBrokenWorkspaceSkillGitExclude(workspace, skills.map((skill) => skill.name));
|
|
30
|
+
return {
|
|
31
|
+
targetKind: 'WORKSPACE',
|
|
32
|
+
compatibilityMode: 'MANAGED_COPY',
|
|
33
|
+
skills: gitExcludeBroken
|
|
34
|
+
? skills.map((skill) => ({ ...skill, localStatus: 'GIT_EXCLUDE_BROKEN' }))
|
|
35
|
+
: skills
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function scanRoot(root, source) {
|
|
40
|
+
let entries;
|
|
41
|
+
try {
|
|
42
|
+
entries = await readdir(root, { withFileTypes: true });
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (isMissing(error))
|
|
46
|
+
return [];
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
const results = [];
|
|
50
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
51
|
+
if (entry.name.startsWith('.mar-'))
|
|
52
|
+
continue;
|
|
53
|
+
const itemPath = path.join(root, entry.name);
|
|
54
|
+
const stat = await lstat(itemPath);
|
|
55
|
+
if (stat.isSymbolicLink() || (!stat.isDirectory() && process.platform === 'win32')) {
|
|
56
|
+
results.push({
|
|
57
|
+
name: entry.name,
|
|
58
|
+
description: '',
|
|
59
|
+
localStatus: 'INVALID_LINK',
|
|
60
|
+
skillFilePath: path.join(itemPath, 'SKILL.md'),
|
|
61
|
+
source
|
|
62
|
+
});
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (!stat.isDirectory())
|
|
66
|
+
continue;
|
|
67
|
+
if (!SKILL_NAME.test(entry.name) || WINDOWS_DEVICE_NAME.test(entry.name)) {
|
|
68
|
+
results.push({
|
|
69
|
+
name: entry.name,
|
|
70
|
+
description: '',
|
|
71
|
+
localStatus: 'INVALID',
|
|
72
|
+
skillFilePath: path.join(itemPath, 'SKILL.md'),
|
|
73
|
+
source
|
|
74
|
+
});
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
results.push({ ...(await inspectSkill(itemPath, entry.name)), source });
|
|
78
|
+
}
|
|
79
|
+
return results;
|
|
80
|
+
}
|
|
81
|
+
async function inspectSkill(directory, directoryName) {
|
|
82
|
+
try {
|
|
83
|
+
const frontmatter = parseSkillFrontmatter(await readBounded(path.join(directory, 'SKILL.md')));
|
|
84
|
+
if (frontmatter.name !== directoryName ||
|
|
85
|
+
!SKILL_NAME.test(frontmatter.name) ||
|
|
86
|
+
WINDOWS_DEVICE_NAME.test(frontmatter.name))
|
|
87
|
+
return {
|
|
88
|
+
name: directoryName,
|
|
89
|
+
description: frontmatter.description,
|
|
90
|
+
localStatus: 'INVALID',
|
|
91
|
+
skillFilePath: path.join(directory, 'SKILL.md')
|
|
92
|
+
};
|
|
93
|
+
const install = await readInstallMetadata(path.join(directory, '.mar-skill-install.json'));
|
|
94
|
+
return {
|
|
95
|
+
name: directoryName,
|
|
96
|
+
description: frontmatter.description,
|
|
97
|
+
localStatus: install === undefined ? 'UNKNOWN_VERSION' : 'VALID',
|
|
98
|
+
skillFilePath: path.join(directory, 'SKILL.md'),
|
|
99
|
+
...(install === undefined ? {} : { install })
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return {
|
|
104
|
+
name: directoryName,
|
|
105
|
+
description: '',
|
|
106
|
+
localStatus: 'INVALID',
|
|
107
|
+
skillFilePath: path.join(directory, 'SKILL.md')
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function readInstallMetadata(file) {
|
|
112
|
+
try {
|
|
113
|
+
return skillInstallMetadataSchema.parse(JSON.parse(await readBounded(file)));
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (isMissing(error))
|
|
117
|
+
return undefined;
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function readBounded(file) {
|
|
122
|
+
const handle = await open(file, 'r');
|
|
123
|
+
try {
|
|
124
|
+
const stat = await handle.stat();
|
|
125
|
+
if (!stat.isFile() || stat.size > MAX_METADATA_BYTES)
|
|
126
|
+
throw new Error('SKILL_METADATA_INVALID');
|
|
127
|
+
const buffer = Buffer.alloc(stat.size);
|
|
128
|
+
await handle.read(buffer, 0, buffer.length, 0);
|
|
129
|
+
return buffer.toString('utf8');
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
await handle.close();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function parseSkillFrontmatter(content) {
|
|
136
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u.exec(content);
|
|
137
|
+
if (match === null)
|
|
138
|
+
throw new Error('SKILL_METADATA_INVALID');
|
|
139
|
+
const values = new Map();
|
|
140
|
+
for (const line of match[1].split(/\r?\n/u)) {
|
|
141
|
+
const field = /^(name|description):\s*(.*?)\s*$/u.exec(line);
|
|
142
|
+
if (field !== null)
|
|
143
|
+
values.set(field[1], unquote(field[2]));
|
|
144
|
+
}
|
|
145
|
+
const name = values.get('name');
|
|
146
|
+
const description = values.get('description');
|
|
147
|
+
if (name === undefined || description === undefined || description.length > 4096)
|
|
148
|
+
throw new Error('SKILL_METADATA_INVALID');
|
|
149
|
+
return { name, description };
|
|
150
|
+
}
|
|
151
|
+
function unquote(value) {
|
|
152
|
+
if (value.length >= 2 &&
|
|
153
|
+
((value.startsWith('"') && value.endsWith('"')) ||
|
|
154
|
+
(value.startsWith("'") && value.endsWith("'"))))
|
|
155
|
+
return value.slice(1, -1);
|
|
156
|
+
return value;
|
|
157
|
+
}
|
|
158
|
+
function mergeWorkspaceCopies(agents, claude) {
|
|
159
|
+
const byName = new Map();
|
|
160
|
+
for (const item of agents)
|
|
161
|
+
byName.set(item.name, { agents: item });
|
|
162
|
+
for (const item of claude)
|
|
163
|
+
byName.set(item.name, { ...byName.get(item.name), claude: item });
|
|
164
|
+
return [...byName.entries()]
|
|
165
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
166
|
+
.map(([name, copies]) => {
|
|
167
|
+
if (copies.agents === undefined || copies.claude === undefined)
|
|
168
|
+
return {
|
|
169
|
+
...withoutSource((copies.agents ?? copies.claude)),
|
|
170
|
+
name,
|
|
171
|
+
localStatus: 'PARTIAL'
|
|
172
|
+
};
|
|
173
|
+
if (copies.agents.localStatus !== copies.claude.localStatus ||
|
|
174
|
+
JSON.stringify(copies.agents.install) !== JSON.stringify(copies.claude.install))
|
|
175
|
+
return {
|
|
176
|
+
name,
|
|
177
|
+
description: copies.agents.description,
|
|
178
|
+
localStatus: 'PARTIAL',
|
|
179
|
+
skillFilePath: copies.agents.skillFilePath
|
|
180
|
+
};
|
|
181
|
+
return withoutSource(copies.agents);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function mergeNodeCompatibilityRoots(agents, claude) {
|
|
185
|
+
const byName = new Map();
|
|
186
|
+
for (const item of agents)
|
|
187
|
+
byName.set(item.name, { agents: item });
|
|
188
|
+
for (const item of claude)
|
|
189
|
+
byName.set(item.name, { ...byName.get(item.name), claude: item });
|
|
190
|
+
return [...byName.entries()]
|
|
191
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
192
|
+
.map(([name, copies]) => {
|
|
193
|
+
if (copies.agents === undefined || copies.claude === undefined)
|
|
194
|
+
return withoutSource((copies.agents ?? copies.claude));
|
|
195
|
+
if (copies.agents.description !== copies.claude.description ||
|
|
196
|
+
copies.agents.localStatus !== copies.claude.localStatus ||
|
|
197
|
+
JSON.stringify(copies.agents.install) !== JSON.stringify(copies.claude.install))
|
|
198
|
+
return {
|
|
199
|
+
name,
|
|
200
|
+
description: copies.agents.description,
|
|
201
|
+
localStatus: 'PARTIAL',
|
|
202
|
+
skillFilePath: copies.agents.skillFilePath
|
|
203
|
+
};
|
|
204
|
+
return withoutSource(copies.agents);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function withoutSource(entry) {
|
|
208
|
+
return {
|
|
209
|
+
name: entry.name,
|
|
210
|
+
description: entry.description,
|
|
211
|
+
localStatus: entry.localStatus,
|
|
212
|
+
skillFilePath: entry.skillFilePath,
|
|
213
|
+
...(entry.install === undefined ? {} : { install: entry.install })
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
async function nodeCompatibilityMode(agentsRoot, claudeRoot) {
|
|
217
|
+
try {
|
|
218
|
+
const stat = await lstat(claudeRoot);
|
|
219
|
+
if (stat.isSymbolicLink()) {
|
|
220
|
+
const target = await realpath(path.resolve(path.dirname(claudeRoot), await readlink(claudeRoot)));
|
|
221
|
+
const agents = await realpath(agentsRoot);
|
|
222
|
+
return target === agents
|
|
223
|
+
? process.platform === 'win32'
|
|
224
|
+
? 'JUNCTION'
|
|
225
|
+
: 'SYMLINK'
|
|
226
|
+
: 'UNAVAILABLE';
|
|
227
|
+
}
|
|
228
|
+
return stat.isDirectory() ? 'MANAGED_COPY' : 'UNAVAILABLE';
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
if (isMissing(error))
|
|
232
|
+
return 'UNAVAILABLE';
|
|
233
|
+
return 'UNAVAILABLE';
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function isMissing(error) {
|
|
237
|
+
return (error instanceof Error && 'code' in error && error.code === 'ENOENT');
|
|
238
|
+
}
|