@aopslabs/aops 0.2.0
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 +19 -0
- package/NOTICE +7 -0
- package/README.md +203 -0
- package/assets/agent-assets/core/SKILL.md +49 -0
- package/assets/agent-assets/core/references/agentspace/SKILL.md +78 -0
- package/assets/agent-assets/core/references/aops-cli-core/SKILL.md +108 -0
- package/assets/agent-assets/core/references/chatv3/SKILL.md +48 -0
- package/assets/agent-assets/core/references/collaborative-work/SKILL.md +85 -0
- package/assets/agent-assets/core/references/discuss/SKILL.md +66 -0
- package/assets/agent-assets/core/references/docman/SKILL.md +69 -0
- package/assets/agent-assets/core/references/hosted-chat/SKILL.md +64 -0
- package/assets/agent-assets/core/references/mission/SKILL.md +54 -0
- package/assets/agent-assets/core/references/projectman/SKILL.md +86 -0
- package/assets/agent-assets/core/references/sys/SKILL.md +36 -0
- package/assets/agent-assets/core/references/view/SKILL.md +58 -0
- package/assets/agent-assets/core/references/working-disciplines/SKILL.md +69 -0
- package/assets/agent-assets/core/user-guides/agent-assets.md +81 -0
- package/assets/agent-assets/core/user-guides/agentspace.md +1098 -0
- package/assets/agent-assets/core/user-guides/aops-cli.md +1107 -0
- package/assets/agent-assets/core/user-guides/aops-system.md +63 -0
- package/assets/agent-assets/core/user-guides/chatv3.md +113 -0
- package/assets/agent-assets/core/user-guides/docman.md +162 -0
- package/assets/agent-assets/core/user-guides/projectman.md +212 -0
- package/assets/agent-assets/core/user-guides/sys.md +94 -0
- package/assets/agent-assets/core/user-guides/working-disciplines.md +588 -0
- package/assets/agent-assets/gateway/aops/SKILL.md +14 -0
- package/assets/r6-remote-session-baseline.json +19 -0
- package/assets/skills/aops-install/SKILL.md +231 -0
- package/assets/skills/aops-install/agents/openai.yaml +4 -0
- package/dist/commands/activity.js +352 -0
- package/dist/commands/agent-profile.js +653 -0
- package/dist/commands/agent.js +710 -0
- package/dist/commands/agents-md.js +125 -0
- package/dist/commands/api.js +84 -0
- package/dist/commands/archive.js +873 -0
- package/dist/commands/artifact.js +535 -0
- package/dist/commands/assets.js +1414 -0
- package/dist/commands/auth/device-login.js +1 -0
- package/dist/commands/auth/legacy-export.js +177 -0
- package/dist/commands/auth/login.js +426 -0
- package/dist/commands/chat.js +1363 -0
- package/dist/commands/chatv3.js +1684 -0
- package/dist/commands/checkpoint.js +549 -0
- package/dist/commands/commercial-artifact.js +475 -0
- package/dist/commands/commercial-license.js +594 -0
- package/dist/commands/community-auth.js +62 -0
- package/dist/commands/community-cockpit.js +185 -0
- package/dist/commands/community-console.js +109 -0
- package/dist/commands/community-doctor.js +335 -0
- package/dist/commands/community-server.js +2062 -0
- package/dist/commands/community-setup.js +284 -0
- package/dist/commands/discuss.js +1448 -0
- package/dist/commands/doc.js +5219 -0
- package/dist/commands/experience.js +922 -0
- package/dist/commands/host.js +1127 -0
- package/dist/commands/init.js +118 -0
- package/dist/commands/memory.js +3129 -0
- package/dist/commands/mission.js +562 -0
- package/dist/commands/official-catalog.js +134 -0
- package/dist/commands/plan.js +84 -0
- package/dist/commands/playbook.js +415 -0
- package/dist/commands/pm/context.js +6 -0
- package/dist/commands/pm/index.js +701 -0
- package/dist/commands/pm/projectman.js +6266 -0
- package/dist/commands/project.js +1080 -0
- package/dist/commands/prompt.js +1024 -0
- package/dist/commands/repo-sync.js +1907 -0
- package/dist/commands/resource.js +527 -0
- package/dist/commands/skill.js +1211 -0
- package/dist/commands/start-disciplines.js +510 -0
- package/dist/commands/start.js +1343 -0
- package/dist/commands/target.js +309 -0
- package/dist/commands/version.js +42 -0
- package/dist/commands/view.js +1852 -0
- package/dist/launchers/cockpit.js +28 -0
- package/dist/launchers/options.js +74 -0
- package/dist/launchers/server.js +41 -0
- package/dist/lib/agent-assets/envelope.js +83 -0
- package/dist/lib/agent-assets/gateway.js +15 -0
- package/dist/lib/agent-assets/guards.js +23 -0
- package/dist/lib/agent-assets/hosted-discovery.js +148 -0
- package/dist/lib/agent-assets/hosted-package-input.js +154 -0
- package/dist/lib/agent-assets/legacy-pointer-migration.js +677 -0
- package/dist/lib/agent-assets/native-fs.js +589 -0
- package/dist/lib/agent-assets/package-manifest.js +324 -0
- package/dist/lib/agent-assets/portable-path.js +67 -0
- package/dist/lib/agent-assets/release-input.js +177 -0
- package/dist/lib/agent-assets/roots.js +45 -0
- package/dist/lib/agent-assets/runtime-binding-reader.js +545 -0
- package/dist/lib/agent-assets/runtime-targets.js +50 -0
- package/dist/lib/agent-assets/store-reader.js +1212 -0
- package/dist/lib/agent-assets/store-types.js +1 -0
- package/dist/lib/agent-assets/store-writer.js +1484 -0
- package/dist/lib/agent-assets/types.js +1 -0
- package/dist/lib/agent-assets/unicode-case-folding-v15-1.generated.js +1548 -0
- package/dist/lib/commercial-final-readiness.js +504 -0
- package/dist/lib/commercial-lifecycle-smoke-isolation.js +236 -0
- package/dist/lib/commercial-managed-postgres-docker-adapter.js +274 -0
- package/dist/lib/commercial-setup-readiness-adapter.js +179 -0
- package/dist/lib/community-client-contract.js +164 -0
- package/dist/lib/community-cockpit-lifecycle.js +601 -0
- package/dist/lib/community-command-abort.js +59 -0
- package/dist/lib/community-diagnostic.js +237 -0
- package/dist/lib/community-docker-adapter.js +498 -0
- package/dist/lib/community-home.js +31 -0
- package/dist/lib/community-instance-contract.js +148 -0
- package/dist/lib/community-instance-layout.js +97 -0
- package/dist/lib/community-lifecycle.js +1631 -0
- package/dist/lib/community-migration-snapshot.js +770 -0
- package/dist/lib/community-native-application-recovery.js +425 -0
- package/dist/lib/community-native-child.js +240 -0
- package/dist/lib/community-native-database-recovery.js +456 -0
- package/dist/lib/community-native-lifecycle.js +2458 -0
- package/dist/lib/community-native-migration.js +339 -0
- package/dist/lib/community-native-postgres.js +596 -0
- package/dist/lib/community-operation-journal.js +773 -0
- package/dist/lib/community-operation-lock.js +596 -0
- package/dist/lib/community-release-resolver.js +291 -0
- package/dist/lib/community-release-verifier.js +475 -0
- package/dist/lib/community-repo-discovery.js +252 -0
- package/dist/lib/community-setup-server-env.js +203 -0
- package/dist/lib/external-url.js +110 -0
- package/dist/lib/official-catalog-gateway.js +108 -0
- package/dist/lib/official-catalog.js +364 -0
- package/dist/lib/server-bootstrap.js +240 -0
- package/dist/lib/setup-agent-assets-bridge.js +176 -0
- package/dist/lib/setup-agent-assets-release.js +52 -0
- package/dist/lib/setup-external-postgres.js +108 -0
- package/dist/lib/setup-init-orchestrator.js +990 -0
- package/dist/lib/setup-install-guide.js +52 -0
- package/dist/lib/setup-installer-release.js +47 -0
- package/dist/lib/setup-local-postgres.js +329 -0
- package/dist/lib/setup-official-catalog-bridge.js +148 -0
- package/dist/lib/setup-readiness.js +888 -0
- package/dist/lib/tui-launcher.js +59 -0
- package/dist/main.js +150 -0
- package/dist/seeds/agents-md.js +54 -0
- package/dist/utils/agent-gateway.js +275 -0
- package/dist/utils/agents-md.js +281 -0
- package/dist/utils/api.js +255 -0
- package/dist/utils/chatv3-session-store.js +185 -0
- package/dist/utils/command.js +67 -0
- package/dist/utils/community-host-runtime.js +376 -0
- package/dist/utils/config.js +1015 -0
- package/dist/utils/discussion-workspace.js +552 -0
- package/dist/utils/experience-workspace.js +326 -0
- package/dist/utils/guide-paths.js +67 -0
- package/dist/utils/hosted-sugar.js +56 -0
- package/dist/utils/hosted-workspace.js +229 -0
- package/dist/utils/memory-workspace.js +537 -0
- package/dist/utils/operator-runtime.js +199 -0
- package/dist/utils/password-crypto.js +39 -0
- package/dist/utils/playbook-workspace.js +479 -0
- package/dist/utils/project-context.js +255 -0
- package/dist/utils/prompts.js +70 -0
- package/dist/utils/refresh-race.js +28 -0
- package/dist/utils/repo-config.js +109 -0
- package/dist/utils/repo-first-projectman.js +194 -0
- package/dist/utils/repo-first-storage.js +13 -0
- package/dist/utils/repo-first-view.js +305 -0
- package/dist/utils/session-guidance.js +278 -0
- package/dist/utils/session-state.js +137 -0
- package/dist/utils/table.js +39 -0
- package/dist/utils/transient-fs.js +49 -0
- package/dist/utils/workspace-root.js +1 -0
- package/launchers/aops-cockpit.ps1 +15 -0
- package/launchers/aops-cockpit.sh +16 -0
- package/launchers/aops-server.ps1 +15 -0
- package/launchers/aops-server.sh +16 -0
- package/native/tui/win32-x64/aops-tui.exe +0 -0
- package/package.json +81 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { startCommunityNativeCockpit } from '../lib/community-cockpit-lifecycle.js';
|
|
3
|
+
import { communityTerminalLauncherHelp, isCommunityTerminalDirectExecution, parseCommunityTerminalLauncherOptions, } from './options.js';
|
|
4
|
+
const defaultRuntime = Object.freeze({
|
|
5
|
+
startCockpit: (options) => startCommunityNativeCockpit(options),
|
|
6
|
+
stdout: process.stdout,
|
|
7
|
+
});
|
|
8
|
+
export async function runCommunityCockpitTerminal(argv, runtime = defaultRuntime) {
|
|
9
|
+
const options = parseCommunityTerminalLauncherOptions(argv);
|
|
10
|
+
if (options.help) {
|
|
11
|
+
runtime.stdout.write(communityTerminalLauncherHelp('cockpit'));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const result = await runtime.startCockpit({
|
|
15
|
+
instanceName: options.instance,
|
|
16
|
+
dataRoot: options.dataRoot,
|
|
17
|
+
});
|
|
18
|
+
runtime.stdout.write(result.action === 'started'
|
|
19
|
+
? `AOPS Cockpit started independently at ${result.origin}. API proxy: ${result.serverOrigin}\n`
|
|
20
|
+
: `AOPS Cockpit is already running at ${result.origin}.\n`);
|
|
21
|
+
}
|
|
22
|
+
if (isCommunityTerminalDirectExecution(import.meta.url)) {
|
|
23
|
+
void runCommunityCockpitTerminal(process.argv.slice(2)).catch((error) => {
|
|
24
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
25
|
+
process.stderr.write(`AOPS Cockpit launcher failed: ${message}\n`);
|
|
26
|
+
process.exitCode = 1;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
export function isCommunityTerminalDirectExecution(moduleUrl, argvEntry = process.argv[1]) {
|
|
5
|
+
if (typeof argvEntry !== 'string')
|
|
6
|
+
return false;
|
|
7
|
+
try {
|
|
8
|
+
const entryPath = realpathSync.native(path.resolve(argvEntry));
|
|
9
|
+
const modulePath = realpathSync.native(fileURLToPath(moduleUrl));
|
|
10
|
+
return path.relative(entryPath, modulePath) === '' && path.relative(modulePath, entryPath) === '';
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function readOptionValue(argv, index, option) {
|
|
17
|
+
const value = argv[index + 1];
|
|
18
|
+
if (!value || value.startsWith('-'))
|
|
19
|
+
throw new Error(`terminal_launcher_option_value_missing:${option}`);
|
|
20
|
+
return { value, nextIndex: index + 1 };
|
|
21
|
+
}
|
|
22
|
+
export function parseCommunityTerminalLauncherOptions(argv) {
|
|
23
|
+
let instance;
|
|
24
|
+
let dataRoot;
|
|
25
|
+
let help = false;
|
|
26
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
27
|
+
const argument = argv[index];
|
|
28
|
+
if (argument === '--help' || argument === '-h') {
|
|
29
|
+
help = true;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (argument === '--instance') {
|
|
33
|
+
if (instance !== undefined)
|
|
34
|
+
throw new Error('terminal_launcher_option_duplicate:--instance');
|
|
35
|
+
const parsed = readOptionValue(argv, index, '--instance');
|
|
36
|
+
instance = parsed.value;
|
|
37
|
+
index = parsed.nextIndex;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (argument.startsWith('--instance=')) {
|
|
41
|
+
if (instance !== undefined)
|
|
42
|
+
throw new Error('terminal_launcher_option_duplicate:--instance');
|
|
43
|
+
instance = argument.slice('--instance='.length);
|
|
44
|
+
if (!instance)
|
|
45
|
+
throw new Error('terminal_launcher_option_value_missing:--instance');
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (argument === '--data-root') {
|
|
49
|
+
if (dataRoot !== undefined)
|
|
50
|
+
throw new Error('terminal_launcher_option_duplicate:--data-root');
|
|
51
|
+
const parsed = readOptionValue(argv, index, '--data-root');
|
|
52
|
+
dataRoot = parsed.value;
|
|
53
|
+
index = parsed.nextIndex;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (argument.startsWith('--data-root=')) {
|
|
57
|
+
if (dataRoot !== undefined)
|
|
58
|
+
throw new Error('terminal_launcher_option_duplicate:--data-root');
|
|
59
|
+
dataRoot = argument.slice('--data-root='.length);
|
|
60
|
+
if (!dataRoot)
|
|
61
|
+
throw new Error('terminal_launcher_option_value_missing:--data-root');
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
throw new Error(`terminal_launcher_option_unknown:${argument}`);
|
|
65
|
+
}
|
|
66
|
+
return Object.freeze({ instance, dataRoot, help });
|
|
67
|
+
}
|
|
68
|
+
export function communityTerminalLauncherHelp(service) {
|
|
69
|
+
const executable = service === 'server' ? 'aops-server' : 'aops-cockpit';
|
|
70
|
+
const purpose = service === 'server'
|
|
71
|
+
? 'Run AOPS Server in this terminal with live logs. Ctrl+C stops it.'
|
|
72
|
+
: 'Start Cockpit independently on its own loopback port.';
|
|
73
|
+
return `${purpose}\n\nUsage:\n ${executable} [--instance <name>] [--data-root <absolute-path>]\n`;
|
|
74
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runCommunityServerStart, } from '../commands/community-server.js';
|
|
3
|
+
import { communityTerminalLauncherHelp, isCommunityTerminalDirectExecution, parseCommunityTerminalLauncherOptions, } from './options.js';
|
|
4
|
+
const defaultRuntime = Object.freeze({
|
|
5
|
+
startServer: (options) => runCommunityServerStart(options),
|
|
6
|
+
stdout: process.stdout,
|
|
7
|
+
});
|
|
8
|
+
export async function runCommunityServerTerminal(argv, runtime = defaultRuntime) {
|
|
9
|
+
const options = parseCommunityTerminalLauncherOptions(argv);
|
|
10
|
+
if (options.help) {
|
|
11
|
+
runtime.stdout.write(communityTerminalLauncherHelp('server'));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
runtime.stdout.write('Starting AOPS Server in this terminal. Live server logs follow; press Ctrl+C to stop.\n');
|
|
15
|
+
await runtime.startServer({
|
|
16
|
+
instance: options.instance,
|
|
17
|
+
dataRoot: options.dataRoot,
|
|
18
|
+
foreground: true,
|
|
19
|
+
silent: true,
|
|
20
|
+
resultSink: (result) => {
|
|
21
|
+
const value = result;
|
|
22
|
+
const origin = typeof value.origin === 'string' ? ` at ${value.origin}` : '';
|
|
23
|
+
const pid = typeof value.pid === 'number' ? ` (PID ${value.pid})` : '';
|
|
24
|
+
runtime.stdout.write(`AOPS Server is running${origin}${pid}.\n`);
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function errorMessage(error) {
|
|
29
|
+
return error instanceof Error ? error.message : String(error);
|
|
30
|
+
}
|
|
31
|
+
if (isCommunityTerminalDirectExecution(import.meta.url)) {
|
|
32
|
+
void runCommunityServerTerminal(process.argv.slice(2)).catch((error) => {
|
|
33
|
+
const message = errorMessage(error);
|
|
34
|
+
if (message === 'community_operation_aborted') {
|
|
35
|
+
process.stdout.write('AOPS Server stopped.\n');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
process.stderr.write(`AOPS Server terminal launcher failed: ${message}\n`);
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export const AGENT_ASSETS_SURFACE = 'agent-assets-client-v1';
|
|
2
|
+
export const AGENT_ASSETS_ERROR_CODES = [
|
|
3
|
+
'not_found',
|
|
4
|
+
'ambiguous',
|
|
5
|
+
'remote_unavailable',
|
|
6
|
+
'hash_mismatch',
|
|
7
|
+
'untrusted_origin',
|
|
8
|
+
'expected_manifest_required',
|
|
9
|
+
'invalid_package_path',
|
|
10
|
+
'link_unsafe_path',
|
|
11
|
+
'binding_conflict',
|
|
12
|
+
'concurrent_writer',
|
|
13
|
+
'different_machine_store',
|
|
14
|
+
'store_identity_mismatch',
|
|
15
|
+
'publication_conflict',
|
|
16
|
+
'atomic_replace_blocked',
|
|
17
|
+
'atomic_primitive_unavailable',
|
|
18
|
+
'durability_unavailable',
|
|
19
|
+
'recovery_confirmation_required',
|
|
20
|
+
'schema_incompatible',
|
|
21
|
+
'rollback_unavailable',
|
|
22
|
+
];
|
|
23
|
+
export class AgentAssetsError extends Error {
|
|
24
|
+
code;
|
|
25
|
+
nextActions;
|
|
26
|
+
details;
|
|
27
|
+
constructor(code, message, options) {
|
|
28
|
+
super(message, { cause: options.cause });
|
|
29
|
+
this.name = 'AgentAssetsError';
|
|
30
|
+
this.code = code;
|
|
31
|
+
this.nextActions = Object.freeze([...options.nextActions]);
|
|
32
|
+
this.details = options.details === undefined ? undefined : Object.freeze({ ...options.details });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function agentAssetsSuccess(input) {
|
|
36
|
+
return Object.freeze({
|
|
37
|
+
schemaVersion: 1,
|
|
38
|
+
command: input.command,
|
|
39
|
+
surface: AGENT_ASSETS_SURFACE,
|
|
40
|
+
ok: true,
|
|
41
|
+
result: input.result,
|
|
42
|
+
diagnostics: Object.freeze([...(input.diagnostics ?? [])]),
|
|
43
|
+
nextActions: Object.freeze([...(input.nextActions ?? [])]),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export function agentAssetsFailure(command, error, diagnostics = []) {
|
|
47
|
+
const normalized = error instanceof AgentAssetsError
|
|
48
|
+
? error
|
|
49
|
+
: new AgentAssetsError('schema_incompatible', error instanceof Error ? error.message : String(error), {
|
|
50
|
+
nextActions: ['Run `aops assets status --verify quick --json` and inspect the reported diagnostics.'],
|
|
51
|
+
cause: error,
|
|
52
|
+
});
|
|
53
|
+
const commandError = Object.freeze({
|
|
54
|
+
code: normalized.code,
|
|
55
|
+
message: normalized.message,
|
|
56
|
+
nextActions: normalized.nextActions,
|
|
57
|
+
...(normalized.details === undefined ? {} : { details: normalized.details }),
|
|
58
|
+
});
|
|
59
|
+
return Object.freeze({
|
|
60
|
+
schemaVersion: 1,
|
|
61
|
+
command,
|
|
62
|
+
surface: AGENT_ASSETS_SURFACE,
|
|
63
|
+
ok: false,
|
|
64
|
+
error: commandError,
|
|
65
|
+
diagnostics: Object.freeze([...diagnostics]),
|
|
66
|
+
nextActions: normalized.nextActions,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export function assertBoundedDiscoveryEnvelope(envelope, candidateCount) {
|
|
70
|
+
if (candidateCount > 5) {
|
|
71
|
+
throw new AgentAssetsError('schema_incompatible', 'Discovery returned more than five metadata candidates.', {
|
|
72
|
+
nextActions: ['Retry with `--limit 5` or lower.'],
|
|
73
|
+
details: { candidateCount, maximum: 5 },
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const byteLength = Buffer.byteLength(JSON.stringify(envelope.result), 'utf8');
|
|
77
|
+
if (byteLength > 2_048) {
|
|
78
|
+
throw new AgentAssetsError('schema_incompatible', 'Discovery metadata result exceeds the 2 KiB pre-body budget.', {
|
|
79
|
+
nextActions: ['Narrow the query or reduce `--limit`; do not load candidate bodies to compensate.'],
|
|
80
|
+
details: { byteLength, maximumBytes: 2_048 },
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
export const AOPS_AGENT_ASSETS_GATEWAY_RELATIVE_PATH = 'skills/aops/SKILL.md';
|
|
4
|
+
const BUNDLED_GATEWAY = typeof __AOPS_AGENT_ASSETS_GATEWAY_TEXT__ === 'undefined'
|
|
5
|
+
? undefined
|
|
6
|
+
: __AOPS_AGENT_ASSETS_GATEWAY_TEXT__;
|
|
7
|
+
export const AOPS_AGENT_ASSETS_GATEWAY_SOURCE_URL = BUNDLED_GATEWAY === undefined
|
|
8
|
+
? new URL('../../../assets/agent-assets/gateway/aops/SKILL.md', import.meta.url)
|
|
9
|
+
: undefined;
|
|
10
|
+
export const AOPS_AGENT_ASSETS_GATEWAY = BUNDLED_GATEWAY
|
|
11
|
+
?? fs.readFileSync(AOPS_AGENT_ASSETS_GATEWAY_SOURCE_URL, 'utf8');
|
|
12
|
+
export const AOPS_AGENT_ASSETS_GATEWAY_SHA256 = crypto
|
|
13
|
+
.createHash('sha256')
|
|
14
|
+
.update(AOPS_AGENT_ASSETS_GATEWAY, 'utf8')
|
|
15
|
+
.digest('hex');
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AgentAssetsError } from './envelope.js';
|
|
2
|
+
export function resolveAgentAssetsMutationGuard(input) {
|
|
3
|
+
if (!input.apply) {
|
|
4
|
+
const confirmFlag = input.destructive ? ' --confirm' : '';
|
|
5
|
+
return Object.freeze({
|
|
6
|
+
mode: 'preview',
|
|
7
|
+
mutationFree: true,
|
|
8
|
+
action: input.action,
|
|
9
|
+
nextActions: Object.freeze([
|
|
10
|
+
`Review this preview, then re-run with --apply${confirmFlag} to execute it.`,
|
|
11
|
+
]),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
if (input.destructive && !input.confirm) {
|
|
15
|
+
throw new AgentAssetsError('recovery_confirmation_required', `${input.action} requires both --apply and --confirm.`, { nextActions: [`Re-run ${input.action} with --apply --confirm after reviewing status and the preview.`] });
|
|
16
|
+
}
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
mode: 'apply',
|
|
19
|
+
mutationFree: false,
|
|
20
|
+
action: input.action,
|
|
21
|
+
nextActions: Object.freeze([]),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { invokeHostedToolWithApiState, unwrapHostedToolResult } from '../../utils/agent-gateway.js';
|
|
2
|
+
import { createCliApiClientFromOptions } from '../../utils/api.js';
|
|
3
|
+
import { AgentAssetsError } from './envelope.js';
|
|
4
|
+
const MATCH_FIELD = /^(?:name|shortDescription|description|tags|version|entryFile|skillStandard|meta\.[A-Za-z0-9_.-]+)$/;
|
|
5
|
+
const EXACT_REF = /^skill-version:(.+)$/;
|
|
6
|
+
const SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
7
|
+
const FORBIDDEN_BODY_KEYS = new Set([
|
|
8
|
+
'content',
|
|
9
|
+
'files',
|
|
10
|
+
'filesystemPath',
|
|
11
|
+
'materializedPath',
|
|
12
|
+
'outputDir',
|
|
13
|
+
'packagePath',
|
|
14
|
+
'serverPath',
|
|
15
|
+
'sourcePath',
|
|
16
|
+
]);
|
|
17
|
+
function discoveryError(message, details) {
|
|
18
|
+
return new AgentAssetsError('schema_incompatible', message, {
|
|
19
|
+
nextActions: [
|
|
20
|
+
'Inspect `aops-cli agent schema --tool agentspace.skill.search --summary`; do not load skill bodies to compensate.',
|
|
21
|
+
],
|
|
22
|
+
...(details === undefined ? {} : { details }),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
function unwrapData(value) {
|
|
29
|
+
let current = value;
|
|
30
|
+
for (let depth = 0; depth < 3; depth += 1) {
|
|
31
|
+
if (!isRecord(current) || !Object.prototype.hasOwnProperty.call(current, 'data'))
|
|
32
|
+
break;
|
|
33
|
+
current = current.data;
|
|
34
|
+
}
|
|
35
|
+
return current;
|
|
36
|
+
}
|
|
37
|
+
function assertMetadataOnly(value) {
|
|
38
|
+
if (!value || typeof value !== 'object')
|
|
39
|
+
return;
|
|
40
|
+
for (const [key, child] of Object.entries(value)) {
|
|
41
|
+
if (FORBIDDEN_BODY_KEYS.has(key)) {
|
|
42
|
+
throw new AgentAssetsError('untrusted_origin', `Hosted discovery returned forbidden body/path field: ${key}.`, {
|
|
43
|
+
nextActions: ['Use only the raw metadata `agentspace.skill.search` Community projection.'],
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
assertMetadataOnly(child);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function boundedString(record, key, maximum, required = true) {
|
|
50
|
+
const value = record[key];
|
|
51
|
+
if (value === undefined && !required)
|
|
52
|
+
return undefined;
|
|
53
|
+
if (typeof value !== 'string' || value.length < 1 || value.length > maximum) {
|
|
54
|
+
throw discoveryError(`Hosted discovery candidate.${key} is invalid or exceeds its metadata budget.`);
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
export function validateHostedSkillDiscovery(payload, options) {
|
|
59
|
+
assertMetadataOnly(payload);
|
|
60
|
+
const candidate = unwrapData(payload);
|
|
61
|
+
if (!isRecord(candidate))
|
|
62
|
+
throw discoveryError('Hosted skill search result must be an object.');
|
|
63
|
+
const query = boundedString(candidate, 'query', 256);
|
|
64
|
+
const normalizedQuery = boundedString(candidate, 'normalizedQuery', 256);
|
|
65
|
+
if (!Array.isArray(candidate.candidates) || candidate.candidates.length > options.limit || candidate.candidates.length > 5) {
|
|
66
|
+
throw discoveryError('Hosted skill search exceeded the requested candidate limit.', {
|
|
67
|
+
requestedLimit: options.limit,
|
|
68
|
+
candidateCount: Array.isArray(candidate.candidates) ? candidate.candidates.length : null,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (candidate.count !== candidate.candidates.length) {
|
|
72
|
+
throw discoveryError('Hosted skill search count does not match its candidate array.');
|
|
73
|
+
}
|
|
74
|
+
const candidates = candidate.candidates.map((entry, index) => {
|
|
75
|
+
if (!isRecord(entry))
|
|
76
|
+
throw discoveryError(`Hosted skill search candidate ${index} must be an object.`);
|
|
77
|
+
const versionId = boundedString(entry, 'versionId', 160);
|
|
78
|
+
const exactRef = boundedString(entry, 'exactRef', 180);
|
|
79
|
+
const match = EXACT_REF.exec(exactRef);
|
|
80
|
+
if (!match || match[1] !== versionId) {
|
|
81
|
+
throw discoveryError(`Hosted skill search candidate ${index} has inconsistent exact identity.`);
|
|
82
|
+
}
|
|
83
|
+
if (entry.origin !== 'hosted')
|
|
84
|
+
throw discoveryError(`Hosted skill search candidate ${index} has an invalid origin.`);
|
|
85
|
+
const packageSha256 = boundedString(entry, 'packageSha256', 64);
|
|
86
|
+
const contentSha256 = boundedString(entry, 'contentSha256', 64);
|
|
87
|
+
if (!SHA256_HEX.test(packageSha256) || !SHA256_HEX.test(contentSha256)) {
|
|
88
|
+
throw discoveryError(`Hosted skill search candidate ${index} has invalid immutable digest metadata.`);
|
|
89
|
+
}
|
|
90
|
+
if (entry.computedTrustClass !== 'verified-hosted-package') {
|
|
91
|
+
throw discoveryError(`Hosted skill search candidate ${index} has an invalid computed trust class.`);
|
|
92
|
+
}
|
|
93
|
+
if (!Number.isSafeInteger(entry.score) || entry.score < 1) {
|
|
94
|
+
throw discoveryError(`Hosted skill search candidate ${index} has an invalid score.`);
|
|
95
|
+
}
|
|
96
|
+
if (!Array.isArray(entry.matchedBy)
|
|
97
|
+
|| entry.matchedBy.length < 1
|
|
98
|
+
|| entry.matchedBy.length > 5
|
|
99
|
+
|| entry.matchedBy.some((field) => typeof field !== 'string' || !MATCH_FIELD.test(field))
|
|
100
|
+
|| new Set(entry.matchedBy).size !== entry.matchedBy.length) {
|
|
101
|
+
throw discoveryError(`Hosted skill search candidate ${index} has invalid match metadata.`);
|
|
102
|
+
}
|
|
103
|
+
return Object.freeze({
|
|
104
|
+
name: boundedString(entry, 'name', 80),
|
|
105
|
+
version: boundedString(entry, 'version', 80),
|
|
106
|
+
versionId,
|
|
107
|
+
exactRef,
|
|
108
|
+
packageSha256,
|
|
109
|
+
contentSha256,
|
|
110
|
+
...(entry.shortDescription === undefined
|
|
111
|
+
? {}
|
|
112
|
+
: { shortDescription: boundedString(entry, 'shortDescription', 96, false) }),
|
|
113
|
+
origin: 'hosted-cache',
|
|
114
|
+
computedTrustClass: 'verified-hosted-package',
|
|
115
|
+
score: entry.score,
|
|
116
|
+
matchedBy: Object.freeze([...entry.matchedBy]),
|
|
117
|
+
rationale: boundedString(entry, 'rationale', 160),
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
return Object.freeze({ query, normalizedQuery, candidates: Object.freeze(candidates) });
|
|
121
|
+
}
|
|
122
|
+
export async function discoverHostedSkills(options) {
|
|
123
|
+
let payload;
|
|
124
|
+
try {
|
|
125
|
+
const input = Object.freeze({ query: options.query, limit: options.limit });
|
|
126
|
+
if (options.invoke) {
|
|
127
|
+
payload = await options.invoke('agentspace.skill.search', input);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const apiState = await createCliApiClientFromOptions({ apiBaseUrl: options.apiBaseUrl });
|
|
131
|
+
const response = await invokeHostedToolWithApiState(apiState, {
|
|
132
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
133
|
+
toolId: 'agentspace.skill.search',
|
|
134
|
+
input,
|
|
135
|
+
});
|
|
136
|
+
payload = unwrapHostedToolResult(response);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
if (error instanceof AgentAssetsError)
|
|
141
|
+
throw error;
|
|
142
|
+
throw new AgentAssetsError('remote_unavailable', 'Hosted skill metadata search failed.', {
|
|
143
|
+
nextActions: ['Retry when the selected AOPS server is reachable, or use `--offline` for the installed core only.'],
|
|
144
|
+
cause: error,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return validateHostedSkillDiscovery(payload, options);
|
|
148
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { invokeHostedToolWithApiState, unwrapHostedToolResult } from '../../utils/agent-gateway.js';
|
|
4
|
+
import { createCliApiClientFromOptions } from '../../utils/api.js';
|
|
5
|
+
import { AgentAssetsError } from './envelope.js';
|
|
6
|
+
import { validatePortablePackageV1 } from './package-manifest.js';
|
|
7
|
+
import { canonicalJsonV1 } from './store-reader.js';
|
|
8
|
+
const FORBIDDEN_SERVER_PATH_KEYS = new Set([
|
|
9
|
+
'filesystemPath',
|
|
10
|
+
'materializedPath',
|
|
11
|
+
'outputDir',
|
|
12
|
+
'packagePath',
|
|
13
|
+
'serverPath',
|
|
14
|
+
'sourcePath',
|
|
15
|
+
]);
|
|
16
|
+
function packageError(code, message, details) {
|
|
17
|
+
return new AgentAssetsError(code, message, {
|
|
18
|
+
nextActions: [
|
|
19
|
+
'Inspect the exact hosted contract with `aops-cli agent schema --tool agentspace.skill-version.export-skill-package --summary`.',
|
|
20
|
+
],
|
|
21
|
+
...(details === undefined ? {} : { details }),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function isRecord(value) {
|
|
25
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
26
|
+
}
|
|
27
|
+
function unwrapData(value) {
|
|
28
|
+
let current = value;
|
|
29
|
+
for (let depth = 0; depth < 3; depth += 1) {
|
|
30
|
+
if (!isRecord(current) || !Object.prototype.hasOwnProperty.call(current, 'data'))
|
|
31
|
+
break;
|
|
32
|
+
current = current.data;
|
|
33
|
+
}
|
|
34
|
+
return current;
|
|
35
|
+
}
|
|
36
|
+
function assertNoServerFilesystemPointers(value) {
|
|
37
|
+
if (!value || typeof value !== 'object')
|
|
38
|
+
return;
|
|
39
|
+
for (const [key, child] of Object.entries(value)) {
|
|
40
|
+
if (FORBIDDEN_SERVER_PATH_KEYS.has(key) && typeof child === 'string' && child.trim()) {
|
|
41
|
+
throw packageError('untrusted_origin', `Hosted client export leaked a server filesystem field: ${key}.`);
|
|
42
|
+
}
|
|
43
|
+
assertNoServerFilesystemPointers(child);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function nonEmpty(record, key, label) {
|
|
47
|
+
const value = record[key];
|
|
48
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
49
|
+
throw packageError('schema_incompatible', `${label}.${key} must be a non-empty string.`);
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
function readExpectedManifest(reference, versionId) {
|
|
54
|
+
if (!reference.startsWith('@')) {
|
|
55
|
+
if (reference !== `skill-version:${versionId}`) {
|
|
56
|
+
throw packageError('expected_manifest_required', 'Expected manifest must be @file.json or the exact immutable skill-version ref.', {
|
|
57
|
+
expected: `skill-version:${versionId}`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const filePath = path.resolve(reference.slice(1));
|
|
63
|
+
let raw;
|
|
64
|
+
try {
|
|
65
|
+
raw = readFileSync(filePath, 'utf8');
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw packageError('expected_manifest_required', 'Expected manifest file could not be read.', {
|
|
69
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (Buffer.byteLength(raw, 'utf8') > 1024 * 1024) {
|
|
73
|
+
throw packageError('schema_incompatible', 'Expected manifest exceeds the 1 MiB input limit.');
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
return JSON.parse(raw);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw packageError('schema_incompatible', 'Expected manifest is not valid JSON.');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function assertExpectedSkillPackageManifestV1(reference, versionId, manifest) {
|
|
83
|
+
if (manifest.assetKind !== 'skill-package' ||
|
|
84
|
+
manifest.versionId !== versionId ||
|
|
85
|
+
manifest.provenance.reference !== `skill-version:${versionId}`) {
|
|
86
|
+
throw packageError('untrusted_origin', 'Package manifest does not identify the exact immutable skill version.');
|
|
87
|
+
}
|
|
88
|
+
const expectedManifest = readExpectedManifest(reference, versionId);
|
|
89
|
+
if (expectedManifest !== null && canonicalJsonV1(expectedManifest) !== canonicalJsonV1(manifest)) {
|
|
90
|
+
throw packageError('hash_mismatch', 'Immutable manifest differs from the caller-pinned expected manifest.');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export function validateHostedSkillPackageExport(payload, options) {
|
|
94
|
+
assertNoServerFilesystemPointers(payload);
|
|
95
|
+
const candidate = unwrapData(payload);
|
|
96
|
+
if (!isRecord(candidate))
|
|
97
|
+
throw packageError('schema_incompatible', 'Hosted export result must be an object.');
|
|
98
|
+
const skillVersionId = nonEmpty(candidate, 'skillVersionId', 'HostedSkillPackageExport');
|
|
99
|
+
if (skillVersionId !== options.versionId) {
|
|
100
|
+
throw packageError('hash_mismatch', 'Hosted export returned a different immutable skill version.');
|
|
101
|
+
}
|
|
102
|
+
if (!Array.isArray(candidate.files) || candidate.files.length === 0) {
|
|
103
|
+
throw packageError('schema_incompatible', 'Hosted export did not include package files.');
|
|
104
|
+
}
|
|
105
|
+
const transferFiles = candidate.files.map((file, index) => {
|
|
106
|
+
if (!isRecord(file) || typeof file.path !== 'string' || typeof file.content !== 'string') {
|
|
107
|
+
throw packageError('schema_incompatible', `Hosted export file ${index} must contain path and UTF-8 content.`);
|
|
108
|
+
}
|
|
109
|
+
return { path: file.path, bytes: Buffer.from(file.content, 'utf8') };
|
|
110
|
+
});
|
|
111
|
+
const validation = validatePortablePackageV1(candidate.manifest, transferFiles);
|
|
112
|
+
if (!validation.ok) {
|
|
113
|
+
throw packageError('hash_mismatch', 'Hosted package bytes do not match immutable publish-time metadata.', {
|
|
114
|
+
issues: validation.issues,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const manifest = validation.value.normalizedManifest;
|
|
118
|
+
assertExpectedSkillPackageManifestV1(options.expectedManifest, skillVersionId, manifest);
|
|
119
|
+
return {
|
|
120
|
+
skillVersionId,
|
|
121
|
+
skillId: nonEmpty(candidate, 'skillId', 'HostedSkillPackageExport'),
|
|
122
|
+
skillName: nonEmpty(candidate, 'skillName', 'HostedSkillPackageExport'),
|
|
123
|
+
projectId: nonEmpty(candidate, 'projectId', 'HostedSkillPackageExport'),
|
|
124
|
+
scopeId: nonEmpty(candidate, 'scopeId', 'HostedSkillPackageExport'),
|
|
125
|
+
manifest,
|
|
126
|
+
transferFiles,
|
|
127
|
+
validation: validation.value,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export async function pullHostedSkillPackage(options) {
|
|
131
|
+
let payload;
|
|
132
|
+
try {
|
|
133
|
+
if (options.invoke) {
|
|
134
|
+
payload = await options.invoke('agentspace.skill-version.export-skill-package', { id: options.versionId });
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const apiState = await createCliApiClientFromOptions({ apiBaseUrl: options.apiBaseUrl });
|
|
138
|
+
const response = await invokeHostedToolWithApiState(apiState, {
|
|
139
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
140
|
+
toolId: 'agentspace.skill-version.export-skill-package',
|
|
141
|
+
input: { id: options.versionId },
|
|
142
|
+
});
|
|
143
|
+
payload = unwrapHostedToolResult(response);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (error instanceof AgentAssetsError)
|
|
148
|
+
throw error;
|
|
149
|
+
throw packageError('remote_unavailable', 'Hosted skill package export failed.', {
|
|
150
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return validateHostedSkillPackageExport(payload, options);
|
|
154
|
+
}
|