@lesliechan721/aw-plugin-core 0.1.0-beta.6 → 0.1.0-beta.60

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.
Files changed (58) hide show
  1. package/dist/aw-capabilities.d.ts +27 -0
  2. package/dist/aw-capabilities.js +117 -0
  3. package/dist/aw-capabilities.js.map +1 -0
  4. package/dist/catalog-file-modes.d.ts +5 -0
  5. package/dist/catalog-file-modes.js +67 -0
  6. package/dist/catalog-file-modes.js.map +1 -0
  7. package/dist/catalog-workspace-resolution.d.ts +3 -0
  8. package/dist/catalog-workspace-resolution.js +34 -0
  9. package/dist/catalog-workspace-resolution.js.map +1 -0
  10. package/dist/generated/plugin-manifest-v3.d.ts +1532 -0
  11. package/dist/generated/{plugin-manifest-v1.js → plugin-manifest-v3.js} +1 -1
  12. package/dist/generated/plugin-manifest-v3.js.map +1 -0
  13. package/dist/index.d.ts +3 -2
  14. package/dist/index.js +2 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/managed-commands.d.ts +167 -0
  17. package/dist/managed-commands.js +463 -0
  18. package/dist/managed-commands.js.map +1 -0
  19. package/dist/manifest-contract.d.ts +3 -6
  20. package/dist/manifest-contract.js +271 -407
  21. package/dist/manifest-contract.js.map +1 -1
  22. package/dist/manifest-type-contract.d.ts +2 -2
  23. package/dist/manifest.d.ts +12 -8
  24. package/dist/manifest.js +18 -7
  25. package/dist/manifest.js.map +1 -1
  26. package/dist/native-runtime/reader-runtime.mjs +34 -0
  27. package/dist/native-runtime/reader.d.mts +6 -0
  28. package/dist/native-runtime/reader.mjs +11 -0
  29. package/dist/native-tools.d.ts +48 -0
  30. package/dist/native-tools.js +192 -0
  31. package/dist/native-tools.js.map +1 -0
  32. package/dist/plugin-authoring-capabilities.d.ts +1 -1
  33. package/dist/plugin-authoring-capabilities.js +133 -26
  34. package/dist/plugin-authoring-capabilities.js.map +1 -1
  35. package/dist/plugin-config.d.ts +47 -0
  36. package/dist/plugin-config.js +1355 -0
  37. package/dist/plugin-config.js.map +1 -0
  38. package/dist/plugin-requirements.d.ts +34 -0
  39. package/dist/plugin-requirements.js +300 -0
  40. package/dist/plugin-requirements.js.map +1 -0
  41. package/dist/plugin-script-contract.d.ts +11 -12
  42. package/dist/plugin-script-contract.js +44 -71
  43. package/dist/plugin-script-contract.js.map +1 -1
  44. package/dist/runtime-config.d.ts +38 -0
  45. package/dist/runtime-config.js +31 -0
  46. package/dist/runtime-config.js.map +1 -0
  47. package/dist/types.d.ts +266 -73
  48. package/dist/workspace.d.ts +37 -9
  49. package/dist/workspace.js +419 -191
  50. package/dist/workspace.js.map +1 -1
  51. package/package.json +16 -6
  52. package/schemas/plugin-manifest-v3.json +2655 -0
  53. package/dist/generated/plugin-manifest-v1.d.ts +0 -368
  54. package/dist/generated/plugin-manifest-v1.js.map +0 -1
  55. package/dist/plugin-release.d.ts +0 -30
  56. package/dist/plugin-release.js +0 -187
  57. package/dist/plugin-release.js.map +0 -1
  58. package/schemas/plugin-manifest-v1.json +0 -329
@@ -1,3 +1,3 @@
1
1
  /* This file is generated. Run `pnpm generate` in packages/plugin-core to update it. */
2
2
  export {};
3
- //# sourceMappingURL=plugin-manifest-v1.js.map
3
+ //# sourceMappingURL=plugin-manifest-v3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-manifest-v3.js","sourceRoot":"","sources":["../../src/generated/plugin-manifest-v3.ts"],"names":[],"mappings":"AAAA,uFAAuF"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './manifest.js';
2
- export type { PluginManifestSourceV1 } from './generated/plugin-manifest-v1.js';
2
+ export type { PluginManifestSourceV3 } from './generated/plugin-manifest-v3.js';
3
3
  export * from './workspace.js';
4
- export * from './plugin-release.js';
4
+ export * from './catalog-file-modes.js';
5
+ export * from './catalog-workspace-resolution.js';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './manifest.js';
2
2
  export * from './workspace.js';
3
- export * from './plugin-release.js';
3
+ export * from './catalog-file-modes.js';
4
+ export * from './catalog-workspace-resolution.js';
4
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAE7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAE7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,yBAAyB,CAAA;AACvC,cAAc,mCAAmC,CAAA"}
@@ -0,0 +1,167 @@
1
+ import { type NativeToolClosure } from './native-tools.js';
2
+ import type { ArtifactState, CodexSkillDelivery, NormalizedPluginManifest, PlatformName, Scope } from './types.js';
3
+ export interface CommandLaunch {
4
+ runner: 'node' | 'executable';
5
+ entry: string;
6
+ args: string[];
7
+ env: Record<string, string>;
8
+ envFrom: Record<string, string>;
9
+ cwd: 'caller' | 'resource';
10
+ }
11
+ export interface CommandDefinition extends CommandLaunch {
12
+ key: string;
13
+ variant: string;
14
+ }
15
+ export interface ManagedCommandReference {
16
+ set: string;
17
+ key: string;
18
+ variant: string;
19
+ }
20
+ export interface ManagedCommandPermission {
21
+ command: ManagedCommandReference;
22
+ argsPrefix: string[];
23
+ }
24
+ export type PermissionCommand = string | ManagedCommandPermission;
25
+ export type ManagedCommandProducerRef = {
26
+ kind: 'artifact';
27
+ artifactId: string;
28
+ } | {
29
+ kind: 'static-skill';
30
+ platform: PlatformName;
31
+ skillKey: string;
32
+ } | {
33
+ kind: 'package';
34
+ packageKey: string;
35
+ };
36
+ export interface ManagedCommandPackage {
37
+ key: string;
38
+ scope: Scope;
39
+ platforms: [PlatformName, ...PlatformName[]];
40
+ sourceDir: string;
41
+ render?: true;
42
+ refreshKeys: string[];
43
+ }
44
+ export type ManagedCommandSet = {
45
+ set: string;
46
+ producer: ManagedCommandProducerRef;
47
+ mode: 'static';
48
+ entries: CommandDefinition[];
49
+ } | {
50
+ set: string;
51
+ producer: ManagedCommandProducerRef;
52
+ mode: 'dynamic';
53
+ refreshKeys: string[];
54
+ };
55
+ export interface ManagedCommands {
56
+ packages: ManagedCommandPackage[];
57
+ sets: ManagedCommandSet[];
58
+ }
59
+ export interface ProducerIdentity {
60
+ sourceId: string;
61
+ sourceIdentity: string;
62
+ pluginName: string;
63
+ kind: 'artifact' | 'static-skill' | 'package';
64
+ key: string;
65
+ platform: PlatformName | null;
66
+ scope: Scope;
67
+ projectRoot: string | null;
68
+ }
69
+ export interface CommandIdentity {
70
+ schemaVersion: 1;
71
+ producer: ProducerIdentity;
72
+ set: string;
73
+ key: string;
74
+ variant: string;
75
+ }
76
+ export interface InstalledCommandProducer {
77
+ schemaVersion: 1;
78
+ producer: ProducerIdentity;
79
+ sets: Array<{
80
+ set: string;
81
+ state: ArtifactState;
82
+ entries: CommandDefinition[];
83
+ }>;
84
+ nativeTools?: NativeToolClosure;
85
+ }
86
+ export interface UserCommandDefinition {
87
+ schemaVersion: 1;
88
+ id: string;
89
+ name: string;
90
+ packageRevision: string;
91
+ launch: CommandLaunch;
92
+ }
93
+ export type ManagedCommandArtifactSurface = {
94
+ kind: 'platform';
95
+ platform: PlatformName;
96
+ } | {
97
+ kind: 'aw';
98
+ name: 'managed-commands';
99
+ };
100
+ export interface ManagedCommandArtifactTargetDefinition {
101
+ id: string;
102
+ target: 'aw.command-set' | 'aw.command-package';
103
+ kind: 'commandSet' | 'commandPackage';
104
+ surface: ManagedCommandArtifactSurface;
105
+ platform?: PlatformName;
106
+ scopes: Scope[];
107
+ states: ArtifactState[];
108
+ refreshKeys: string[];
109
+ producer: ManagedCommandProducerRef;
110
+ set?: string;
111
+ packageKey?: string;
112
+ delivery?: CodexSkillDelivery;
113
+ }
114
+ export interface CommandSetArtifact {
115
+ id: string;
116
+ target: 'aw.command-set';
117
+ kind: 'commandSet';
118
+ platform?: PlatformName;
119
+ distribution?: never;
120
+ state: ArtifactState;
121
+ refreshKeys: string[];
122
+ entries: CommandDefinition[];
123
+ disabledReason?: string;
124
+ }
125
+ export interface CommandPackageArtifact {
126
+ id: string;
127
+ target: 'aw.command-package';
128
+ kind: 'commandPackage';
129
+ platform?: never;
130
+ distribution?: never;
131
+ state: 'present';
132
+ refreshKeys: string[];
133
+ files: Record<string, string>;
134
+ }
135
+ export declare const COMMAND_PRODUCER_RECORD_FILE = ".aw-command-producer.json";
136
+ export declare const MANAGED_COMMAND_NAME_PATTERN: RegExp;
137
+ export declare function normalizeCommandRelativeFile(value: unknown, field?: string): string;
138
+ export declare function validateCommandEnvironment(env: Record<string, string>, envFrom: Record<string, string>, platform?: NodeJS.Platform): void;
139
+ export declare function normalizeCommandLaunch(value: unknown, field?: string): CommandLaunch;
140
+ export declare function normalizeCommandDefinition(value: unknown, field?: string): CommandDefinition;
141
+ export declare function normalizeCommandDefinitions(value: unknown, field?: string): CommandDefinition[];
142
+ export declare function normalizeManagedCommandReference(value: unknown, field?: string): ManagedCommandReference;
143
+ export declare function normalizeManagedCommandPermission(value: unknown, field?: string): ManagedCommandPermission;
144
+ export declare function normalizeManagedCommands(value: unknown, field?: string): ManagedCommands;
145
+ export declare function normalizeProducerIdentity(value: unknown, field?: string): ProducerIdentity;
146
+ export declare function normalizeCommandIdentity(value: unknown, field?: string): CommandIdentity;
147
+ export declare function canonicalizeCommandIdentity(value: CommandIdentity): string;
148
+ export declare function getPluginCommandId(identity: CommandIdentity): string;
149
+ export declare function getCommandPackageId(producer: ProducerIdentity): string;
150
+ export declare function isManagedCommandId(value: unknown): value is string;
151
+ export declare function isUserCommandId(value: unknown): value is string;
152
+ export declare function isCommandPackageId(value: unknown): value is string;
153
+ export declare function normalizeInstalledCommandProducer(value: unknown): InstalledCommandProducer;
154
+ export declare function normalizeUserCommandDefinition(value: unknown): UserCommandDefinition;
155
+ export interface ManagedCommandPlaceholder {
156
+ start: number;
157
+ end: number;
158
+ reference: ManagedCommandReference;
159
+ }
160
+ export declare function parseManagedCommandPlaceholders(value: string): ManagedCommandPlaceholder[];
161
+ export declare function replaceManagedCommandPlaceholders(value: string, resolve: (reference: ManagedCommandReference) => string): string;
162
+ export declare function validateManagedCommandManifest(manifest: NormalizedPluginManifest): void;
163
+ export declare function getManagedCommandArtifactTargets(manifest: NormalizedPluginManifest): ManagedCommandArtifactTargetDefinition[];
164
+ /** Checks entry path chains after a producer candidate is complete. */
165
+ export declare function validateCommandEntryFiles(resourceRoot: string, entries: readonly CommandDefinition[]): Promise<void>;
166
+ /** Validates the package input closure without following dependency or author-only trees. */
167
+ export declare function validateManagedCommandSources(pluginRoot: string, manifest: NormalizedPluginManifest): Promise<void>;
@@ -0,0 +1,463 @@
1
+ import { createHash } from 'node:crypto';
2
+ import path from 'node:path';
3
+ import fs from 'fs-extra';
4
+ import { getPluginConfigDependencyKeys } from './plugin-config.js';
5
+ import { formatMarketplaceProductsForError, supportsPlatform } from './platform-support.js';
6
+ import { isSkillAuthorOnlyPath } from './skill-authoring.js';
7
+ import { normalizeNativeToolClosure } from './native-tools.js';
8
+ export const COMMAND_PRODUCER_RECORD_FILE = '.aw-command-producer.json';
9
+ export const MANAGED_COMMAND_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/u;
10
+ const ENVIRONMENT_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/u;
11
+ const SAFE_PERMISSION_TOKEN = /^[A-Za-z0-9_./:@%+=,-]+$/u;
12
+ const PLUGIN_COMMAND_ID = /^cmdp_[0-9a-f]{16}$/u;
13
+ const USER_COMMAND_ID = /^cmdu_[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
14
+ const PACKAGE_ID = /^pkg_[0-9a-f]{64}$/u;
15
+ const PACKAGE_PROTECTED_NAMES = new Set(['.git', 'node_modules', '.agents-plugin', '.codex-plugin', '.claude-plugin', '.release', '.tmp', 'evals', 'reports', COMMAND_PRODUCER_RECORD_FILE, '.agent-workflows-owner.json', '.aw-runtime-config.json', 'aw-plugin-requirements.json']);
16
+ const PACKAGE_OUTPUT_ROOTS = new Set(['codex', 'claude', 'agent-skills']);
17
+ function object(value, field, allowed) {
18
+ if (!value || typeof value !== 'object' || Array.isArray(value))
19
+ throw new Error(`Invalid ${field}: expected an object.`);
20
+ for (const key of Object.keys(value)) {
21
+ if (!allowed.includes(key))
22
+ throw new Error(`Invalid ${field}: unknown field ${JSON.stringify(key)}.`);
23
+ }
24
+ return value;
25
+ }
26
+ function text(value, field, maxLength) {
27
+ if (typeof value !== 'string' || /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/u.test(value) || value.includes('\0')) {
28
+ throw new Error(`Invalid ${field}: expected a Unicode string without NUL.`);
29
+ }
30
+ if (maxLength !== undefined && [...value].length > maxLength)
31
+ throw new Error(`Invalid ${field}: string is too long.`);
32
+ return value;
33
+ }
34
+ function nonempty(value, field) {
35
+ const result = text(value, field);
36
+ if (result.length === 0)
37
+ throw new Error(`Invalid ${field}: expected a non-empty string.`);
38
+ return result;
39
+ }
40
+ function name(value, field) {
41
+ const result = text(value, field);
42
+ if (!MANAGED_COMMAND_NAME_PATTERN.test(result))
43
+ throw new Error(`Invalid ${field}: expected a managed command Name.`);
44
+ return result;
45
+ }
46
+ function enumeration(value, field, values) {
47
+ if (!values.includes(value))
48
+ throw new Error(`Invalid ${field}: expected ${values.join(' | ')}.`);
49
+ return value;
50
+ }
51
+ function strings(value, field, unique = false) {
52
+ if (!Array.isArray(value))
53
+ throw new Error(`Invalid ${field}: expected an array.`);
54
+ const result = value.map((entry, index) => text(entry, `${field}[${index}]`));
55
+ if (unique && new Set(result).size !== result.length)
56
+ throw new Error(`Invalid ${field}: duplicate value.`);
57
+ return result;
58
+ }
59
+ export function normalizeCommandRelativeFile(value, field = 'entry') {
60
+ const result = nonempty(value, field);
61
+ if (result.startsWith('/') || /^[A-Za-z]:/u.test(result) || /[\\\u0000-\u001f\u007f-\u009f]/u.test(result)
62
+ || result.split('/').some((segment) => segment === '' || segment === '.' || segment === '..')) {
63
+ throw new Error(`Invalid ${field}: expected a safe relative file path.`);
64
+ }
65
+ return result;
66
+ }
67
+ function environmentMap(value, field, source) {
68
+ const input = object(value, field, Object.keys((value && typeof value === 'object') ? value : {}));
69
+ const result = {};
70
+ for (const [key, entry] of Object.entries(input)) {
71
+ if (!ENVIRONMENT_NAME.test(key))
72
+ throw new Error(`Invalid ${field}: invalid environment name.`);
73
+ const normalized = text(entry, `${field}.${key}`, 4096);
74
+ if (source && !ENVIRONMENT_NAME.test(normalized))
75
+ throw new Error(`Invalid ${field}.${key}: expected an environment name.`);
76
+ Object.defineProperty(result, key, { value: normalized, enumerable: true, configurable: true, writable: true });
77
+ }
78
+ return result;
79
+ }
80
+ function protectedEnvironmentName(value) {
81
+ return /^(?:AW_PLUGIN_|AW_SUBAGENT_|AW_COMMAND_|AW_NATIVE_)/u.test(value.toUpperCase())
82
+ || ['AW_AGENT_SKILL_EVAL_PROCESS_MARKER', 'AW_HOME', 'AW_CODEX_HOME', 'AW_CLAUDE_HOME'].includes(value.toUpperCase());
83
+ }
84
+ export function validateCommandEnvironment(env, envFrom, platform = process.platform) {
85
+ const literal = environmentMap(env, 'env', false);
86
+ const inherited = environmentMap(envFrom, 'envFrom', true);
87
+ for (const key of [...Object.keys(literal), ...Object.keys(inherited), ...Object.values(inherited)]) {
88
+ if (protectedEnvironmentName(key))
89
+ throw new Error('Invalid command environment: protected environment name.');
90
+ }
91
+ const targetKeys = [...Object.keys(literal), ...Object.keys(inherited)].map((key) => platform === 'win32' ? key.toUpperCase() : key);
92
+ if (new Set(targetKeys).size !== targetKeys.length)
93
+ throw new Error('Invalid command environment: duplicate target name.');
94
+ }
95
+ export function normalizeCommandLaunch(value, field = 'command launch') {
96
+ const input = object(value, field, ['runner', 'entry', 'args', 'env', 'envFrom', 'cwd']);
97
+ const args = input.args === undefined ? [] : strings(input.args, `${field}.args`);
98
+ if (args.length > 128 || args.some((arg) => [...arg].length > 4096))
99
+ throw new Error(`Invalid ${field}.args: argument limit exceeded.`);
100
+ const env = input.env === undefined ? {} : environmentMap(input.env, `${field}.env`, false);
101
+ const envFrom = input.envFrom === undefined ? {} : environmentMap(input.envFrom, `${field}.envFrom`, true);
102
+ validateCommandEnvironment(env, envFrom);
103
+ return {
104
+ runner: enumeration(input.runner, `${field}.runner`, ['node', 'executable']),
105
+ entry: normalizeCommandRelativeFile(input.entry, `${field}.entry`), args, env, envFrom,
106
+ cwd: input.cwd === undefined ? 'caller' : enumeration(input.cwd, `${field}.cwd`, ['caller', 'resource']),
107
+ };
108
+ }
109
+ export function normalizeCommandDefinition(value, field = 'command') {
110
+ const input = object(value, field, ['key', 'variant', 'runner', 'entry', 'args', 'env', 'envFrom', 'cwd']);
111
+ const { key, variant, ...launch } = input;
112
+ return { key: name(key, `${field}.key`), variant: variant === undefined ? 'default' : name(variant, `${field}.variant`), ...normalizeCommandLaunch(launch, field) };
113
+ }
114
+ export function normalizeCommandDefinitions(value, field = 'entries') {
115
+ if (!Array.isArray(value))
116
+ throw new Error(`Invalid ${field}: expected an array.`);
117
+ const entries = value.map((entry, index) => normalizeCommandDefinition(entry, `${field}[${index}]`));
118
+ const keys = entries.map((entry) => `${entry.key}/${entry.variant}`);
119
+ if (new Set(keys).size !== keys.length)
120
+ throw new Error(`Invalid ${field}: duplicate command key/variant.`);
121
+ return entries;
122
+ }
123
+ export function normalizeManagedCommandReference(value, field = 'command reference') {
124
+ const input = object(value, field, ['set', 'key', 'variant']);
125
+ return { set: name(input.set, `${field}.set`), key: name(input.key, `${field}.key`), variant: input.variant === undefined ? 'default' : name(input.variant, `${field}.variant`) };
126
+ }
127
+ export function normalizeManagedCommandPermission(value, field = 'command permission') {
128
+ const input = object(value, field, ['command', 'argsPrefix']);
129
+ const argsPrefix = input.argsPrefix === undefined ? [] : strings(input.argsPrefix, `${field}.argsPrefix`);
130
+ if (argsPrefix.some((token) => !SAFE_PERMISSION_TOKEN.test(token)))
131
+ throw new Error(`Invalid ${field}.argsPrefix: expected safe non-empty tokens.`);
132
+ return { command: normalizeManagedCommandReference(input.command, `${field}.command`), argsPrefix };
133
+ }
134
+ function normalizeProducerRef(value, field) {
135
+ const input = object(value, field, ['kind', 'artifactId', 'platform', 'skillKey', 'packageKey']);
136
+ if (input.kind === 'artifact') {
137
+ object(value, field, ['kind', 'artifactId']);
138
+ return { kind: 'artifact', artifactId: nonempty(input.artifactId, `${field}.artifactId`) };
139
+ }
140
+ if (input.kind === 'static-skill') {
141
+ object(value, field, ['kind', 'platform', 'skillKey']);
142
+ const skillKey = normalizeCommandRelativeFile(input.skillKey, `${field}.skillKey`);
143
+ if (skillKey.includes('/'))
144
+ throw new Error(`Invalid ${field}.skillKey: expected a declared Skill key.`);
145
+ return { kind: 'static-skill', platform: enumeration(input.platform, `${field}.platform`, ['codex', 'claude']), skillKey };
146
+ }
147
+ object(value, field, ['kind', 'packageKey']);
148
+ if (input.kind !== 'package')
149
+ throw new Error(`Invalid ${field}.kind.`);
150
+ return { kind: 'package', packageKey: name(input.packageKey, `${field}.packageKey`) };
151
+ }
152
+ export function normalizeManagedCommands(value, field = 'managedCommands') {
153
+ const input = object(value, field, ['packages', 'sets']);
154
+ const packages = input.packages === undefined ? [] : (() => {
155
+ if (!Array.isArray(input.packages))
156
+ throw new Error(`Invalid ${field}.packages: expected an array.`);
157
+ return input.packages.map((entry, index) => {
158
+ const label = `${field}.packages[${index}]`;
159
+ const item = object(entry, label, ['key', 'scope', 'platforms', 'sourceDir', 'render', 'refreshKeys']);
160
+ const platforms = strings(item.platforms, `${label}.platforms`, true).map((platform) => enumeration(platform, `${label}.platforms`, ['codex', 'claude']));
161
+ if (platforms.length === 0)
162
+ throw new Error(`Invalid ${label}.platforms: expected a non-empty array.`);
163
+ if (item.render !== undefined && item.render !== true)
164
+ throw new Error(`Invalid ${label}.render: expected true.`);
165
+ const refreshKeys = item.refreshKeys === undefined ? [] : strings(item.refreshKeys, `${label}.refreshKeys`, true);
166
+ if (item.render !== true && refreshKeys.length > 0)
167
+ throw new Error(`Invalid ${label}.refreshKeys: requires render: true.`);
168
+ const sourceDir = nonempty(item.sourceDir, `${label}.sourceDir`);
169
+ if (!sourceDir.startsWith('./'))
170
+ throw new Error(`Invalid ${label}.sourceDir: expected ./ plugin-relative directory.`);
171
+ normalizeCommandRelativeFile(sourceDir.slice(2), `${label}.sourceDir`);
172
+ const parts = sourceDir.slice(2).split('/');
173
+ if (PACKAGE_OUTPUT_ROOTS.has(parts[0]) || parts.some((part) => PACKAGE_PROTECTED_NAMES.has(part)) || isSkillAuthorOnlyPath(sourceDir.slice(2))) {
174
+ throw new Error(`Invalid ${label}.sourceDir: protected or author-only directory.`);
175
+ }
176
+ return { key: name(item.key, `${label}.key`), scope: enumeration(item.scope, `${label}.scope`, ['user', 'project']), platforms: platforms, sourceDir, ...(item.render === true ? { render: true } : {}), refreshKeys };
177
+ });
178
+ })();
179
+ const sets = input.sets === undefined ? [] : (() => {
180
+ if (!Array.isArray(input.sets))
181
+ throw new Error(`Invalid ${field}.sets: expected an array.`);
182
+ return input.sets.map((entry, index) => {
183
+ const label = `${field}.sets[${index}]`;
184
+ const item = object(entry, label, ['set', 'producer', 'mode', 'entries', 'refreshKeys']);
185
+ const set = name(item.set, `${label}.set`);
186
+ const producer = normalizeProducerRef(item.producer, `${label}.producer`);
187
+ if (item.mode === 'static') {
188
+ object(entry, label, ['set', 'producer', 'mode', 'entries']);
189
+ return { set, producer, mode: 'static', entries: normalizeCommandDefinitions(item.entries, `${label}.entries`) };
190
+ }
191
+ object(entry, label, ['set', 'producer', 'mode', 'refreshKeys']);
192
+ if (item.mode !== 'dynamic')
193
+ throw new Error(`Invalid ${label}.mode.`);
194
+ return { set, producer, mode: 'dynamic', refreshKeys: strings(item.refreshKeys, `${label}.refreshKeys`, true) };
195
+ });
196
+ })();
197
+ if (new Set(packages.map((item) => item.key)).size !== packages.length)
198
+ throw new Error(`Invalid ${field}.packages: duplicate package key.`);
199
+ if (new Set(sets.map((item) => item.set)).size !== sets.length)
200
+ throw new Error(`Invalid ${field}.sets: duplicate set.`);
201
+ return { packages, sets };
202
+ }
203
+ export function normalizeProducerIdentity(value, field = 'producer identity') {
204
+ const input = object(value, field, ['sourceId', 'sourceIdentity', 'pluginName', 'kind', 'key', 'platform', 'scope', 'projectRoot']);
205
+ const sourceId = nonempty(input.sourceId, `${field}.sourceId`);
206
+ if (!/^[a-z][a-z0-9-]{0,31}$/u.test(sourceId))
207
+ throw new Error(`Invalid ${field}.sourceId.`);
208
+ const sourceIdentity = nonempty(input.sourceIdentity, `${field}.sourceIdentity`);
209
+ if (!/^sha256:[0-9a-f]{64}$/u.test(sourceIdentity))
210
+ throw new Error(`Invalid ${field}.sourceIdentity.`);
211
+ const pluginName = nonempty(input.pluginName, `${field}.pluginName`);
212
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(pluginName))
213
+ throw new Error(`Invalid ${field}.pluginName.`);
214
+ const kind = enumeration(input.kind, `${field}.kind`, ['artifact', 'static-skill', 'package']);
215
+ const key = nonempty(input.key, `${field}.key`);
216
+ if (kind === 'package')
217
+ name(key, `${field}.key`);
218
+ const platform = kind === 'package'
219
+ ? (input.platform === null ? null : (() => { throw new Error(`Invalid ${field}.platform: package requires null.`); })())
220
+ : enumeration(input.platform, `${field}.platform`, ['codex', 'claude']);
221
+ const scope = enumeration(input.scope, `${field}.scope`, ['user', 'project']);
222
+ let projectRoot = null;
223
+ if (scope === 'user') {
224
+ if (input.projectRoot !== null)
225
+ throw new Error(`Invalid ${field}.projectRoot: user requires null.`);
226
+ }
227
+ else {
228
+ projectRoot = nonempty(input.projectRoot, `${field}.projectRoot`);
229
+ if (!path.isAbsolute(projectRoot))
230
+ throw new Error(`Invalid ${field}.projectRoot: expected a canonical absolute path.`);
231
+ const normalized = path.normalize(projectRoot);
232
+ const canonical = normalized === path.parse(normalized).root ? normalized : normalized.replace(/[\\/]+$/u, '');
233
+ const driveCanonical = process.platform === 'win32' ? canonical.replace(/^[a-z]:/u, (drive) => drive.toUpperCase()) : canonical;
234
+ if (driveCanonical !== projectRoot)
235
+ throw new Error(`Invalid ${field}.projectRoot: expected a canonical absolute path.`);
236
+ }
237
+ return { sourceId, sourceIdentity, pluginName, kind, key, platform, scope, projectRoot };
238
+ }
239
+ export function normalizeCommandIdentity(value, field = 'command identity') {
240
+ const input = object(value, field, ['schemaVersion', 'producer', 'set', 'key', 'variant']);
241
+ if (input.schemaVersion !== 1)
242
+ throw new Error(`Invalid ${field}.schemaVersion: expected 1.`);
243
+ // Identity records have no defaults: every identity field is part of the hash contract.
244
+ if (input.variant === undefined)
245
+ throw new Error(`Invalid ${field}.variant: required.`);
246
+ return { schemaVersion: 1, producer: normalizeProducerIdentity(input.producer, `${field}.producer`), set: name(input.set, `${field}.set`), key: name(input.key, `${field}.key`), variant: name(input.variant, `${field}.variant`) };
247
+ }
248
+ function canonical(value) {
249
+ if (Array.isArray(value))
250
+ return `[${value.map(canonical).join(',')}]`;
251
+ if (value !== null && typeof value === 'object') {
252
+ const input = value;
253
+ return `{${Object.keys(input).sort().map((key) => `${JSON.stringify(key)}:${canonical(input[key])}`).join(',')}}`;
254
+ }
255
+ return JSON.stringify(value);
256
+ }
257
+ export function canonicalizeCommandIdentity(value) { return canonical(normalizeCommandIdentity(value)); }
258
+ export function getPluginCommandId(identity) {
259
+ return `cmdp_${createHash('sha256').update(canonicalizeCommandIdentity(identity), 'utf8').digest('hex').slice(0, 16)}`;
260
+ }
261
+ export function getCommandPackageId(producer) {
262
+ return `pkg_${createHash('sha256').update(canonical(normalizeProducerIdentity(producer)), 'utf8').digest('hex')}`;
263
+ }
264
+ export function isManagedCommandId(value) { return typeof value === 'string' && (PLUGIN_COMMAND_ID.test(value) || USER_COMMAND_ID.test(value)); }
265
+ export function isUserCommandId(value) { return typeof value === 'string' && USER_COMMAND_ID.test(value); }
266
+ export function isCommandPackageId(value) { return typeof value === 'string' && PACKAGE_ID.test(value); }
267
+ export function normalizeInstalledCommandProducer(value) {
268
+ const input = object(value, 'command producer record', ['schemaVersion', 'producer', 'sets', 'nativeTools']);
269
+ if (input.schemaVersion !== 1 || !Array.isArray(input.sets))
270
+ throw new Error('Invalid command producer record.');
271
+ const sets = input.sets.map((value, index) => {
272
+ const field = `command producer record.sets[${index}]`;
273
+ const item = object(value, field, ['set', 'state', 'entries']);
274
+ if (Array.isArray(item.entries)) {
275
+ for (const entry of item.entries) {
276
+ const definition = object(entry, `${field}.entries`, ['key', 'variant', 'runner', 'entry', 'args', 'env', 'envFrom', 'cwd']);
277
+ if (['variant', 'args', 'env', 'envFrom', 'cwd'].some((key) => definition[key] === undefined))
278
+ throw new Error(`Invalid ${field}.entries: installed definitions must include defaults.`);
279
+ }
280
+ }
281
+ const entries = normalizeCommandDefinitions(item.entries, `${field}.entries`);
282
+ const state = enumeration(item.state, `${field}.state`, ['present', 'disabled']);
283
+ if (state === 'disabled' && entries.length !== 0)
284
+ throw new Error(`Invalid ${field}: disabled set must have no entries.`);
285
+ return { set: name(item.set, `${field}.set`), state, entries };
286
+ });
287
+ if (new Set(sets.map((item) => item.set)).size !== sets.length)
288
+ throw new Error('Invalid command producer record: duplicate set.');
289
+ const nativeTools = input.nativeTools === undefined ? undefined : normalizeNativeToolClosure(input.nativeTools);
290
+ if (nativeTools?.consumers.some((consumer) => consumer.kind !== 'managed-command-set' || !sets.some((set) => set.set === consumer.key))) {
291
+ throw new Error('Invalid command producer record: native consumer must belong to this producer.');
292
+ }
293
+ return { schemaVersion: 1, producer: normalizeProducerIdentity(input.producer), sets, ...(nativeTools ? { nativeTools } : {}) };
294
+ }
295
+ export function normalizeUserCommandDefinition(value) {
296
+ const input = object(value, 'user command definition', ['schemaVersion', 'id', 'name', 'packageRevision', 'launch']);
297
+ if (input.schemaVersion !== 1 || !isUserCommandId(input.id))
298
+ throw new Error('Invalid user command definition identity.');
299
+ const displayName = text(input.name, 'user command definition.name', 128);
300
+ if (displayName.trim().length === 0 || /[\u0000-\u001f\u007f-\u009f]/u.test(displayName))
301
+ throw new Error('Invalid user command definition.name.');
302
+ const packageRevision = text(input.packageRevision, 'user command definition.packageRevision');
303
+ if (!USER_COMMAND_ID.test(`cmdu_${packageRevision}`))
304
+ throw new Error('Invalid user command definition.packageRevision.');
305
+ const launch = object(input.launch, 'user command definition.launch', ['runner', 'entry', 'args', 'env', 'envFrom', 'cwd']);
306
+ if (['args', 'env', 'envFrom', 'cwd'].some((key) => launch[key] === undefined))
307
+ throw new Error('Invalid user command definition.launch: installed launch must include defaults.');
308
+ return { schemaVersion: 1, id: input.id, name: displayName, packageRevision, launch: normalizeCommandLaunch(input.launch) };
309
+ }
310
+ export function parseManagedCommandPlaceholders(value) {
311
+ text(value, 'Skill Markdown');
312
+ const result = [];
313
+ const expression = /\{\{aw\.command:([^}]*)(\}\})?/gu;
314
+ for (const match of value.matchAll(expression)) {
315
+ const parts = match[1].split('/');
316
+ if (match[2] !== '}}' || (parts.length !== 2 && parts.length !== 3))
317
+ throw new Error('commandReferenceInvalid: malformed managed command placeholder.');
318
+ try {
319
+ result.push({ start: match.index, end: match.index + match[0].length, reference: normalizeManagedCommandReference({ set: parts[0], key: parts[1], ...(parts.length === 3 ? { variant: parts[2] } : {}) }) });
320
+ }
321
+ catch {
322
+ throw new Error('commandReferenceInvalid: malformed managed command placeholder.');
323
+ }
324
+ }
325
+ return result;
326
+ }
327
+ export function replaceManagedCommandPlaceholders(value, resolve) {
328
+ let cursor = 0;
329
+ let result = '';
330
+ for (const placeholder of parseManagedCommandPlaceholders(value)) {
331
+ const id = resolve(placeholder.reference);
332
+ if (!isManagedCommandId(id))
333
+ throw new Error('commandReferenceInvalid: resolver returned an invalid command ID.');
334
+ result += value.slice(cursor, placeholder.start) + `aw command run ${id} --`;
335
+ cursor = placeholder.end;
336
+ }
337
+ return result + value.slice(cursor);
338
+ }
339
+ export function validateManagedCommandManifest(manifest) {
340
+ const managed = manifest.managedCommands;
341
+ if (!managed)
342
+ return;
343
+ const validateKeys = (keys, field) => {
344
+ if (keys.length === 0)
345
+ return;
346
+ if (!manifest.config || manifest.config.schemaVersion !== 4)
347
+ throw new Error(`Invalid ${field}: requires Config v4.`);
348
+ for (const key of getPluginConfigDependencyKeys(manifest.config, keys)) {
349
+ if (manifest.config.fields[key]?.sensitive === true)
350
+ throw new Error(`Invalid ${field}: sensitive config dependency is not permitted.`);
351
+ }
352
+ };
353
+ for (const item of managed.packages)
354
+ validateKeys(item.refreshKeys, `managedCommands.packages.${item.key}.refreshKeys`);
355
+ for (const item of managed.sets) {
356
+ if (item.mode === 'dynamic')
357
+ validateKeys(item.refreshKeys, `managedCommands.sets.${item.set}.refreshKeys`);
358
+ const producer = item.producer;
359
+ if (producer.kind === 'package') {
360
+ if (!managed.packages.some((pkg) => pkg.key === producer.packageKey))
361
+ throw new Error(`Invalid managedCommands set ${item.set}: unknown package producer.`);
362
+ }
363
+ else if (producer.kind === 'artifact') {
364
+ const targets = (manifest.artifacts?.targets ?? []).filter((target) => target.id === producer.artifactId);
365
+ if (targets.length !== 1 || !['codex.skill', 'claude.skill'].includes(targets[0].target))
366
+ throw new Error(`Invalid managedCommands set ${item.set}: artifact producer must uniquely identify a platform Skill.`);
367
+ }
368
+ else {
369
+ if (!supportsPlatform(manifest, producer.platform))
370
+ throw new Error(`Invalid managedCommands set ${item.set}: static Skill producer requires ${producer.platform.toUpperCase()} support, but ${formatMarketplaceProductsForError(manifest)}.`);
371
+ if (!manifest.skills || (manifest.artifacts?.targets ?? []).some((target) => target.platform === producer.platform && target.skillKey === producer.skillKey))
372
+ throw new Error(`Invalid managedCommands set ${item.set}: expected a static Skill producer.`);
373
+ }
374
+ }
375
+ if ((managed.packages.some((item) => item.render) || managed.sets.some((item) => item.mode === 'dynamic')) && !manifest.scripts?.artifacts?.render) {
376
+ throw new Error('Invalid managedCommands: dynamic command targets require scripts.artifacts.render.');
377
+ }
378
+ }
379
+ export function getManagedCommandArtifactTargets(manifest) {
380
+ validateManagedCommandManifest(manifest);
381
+ const managed = manifest.managedCommands;
382
+ if (!managed)
383
+ return [];
384
+ const result = managed.packages.filter((pkg) => pkg.render).map((pkg) => ({
385
+ id: `command-package.${pkg.key}`, target: 'aw.command-package', kind: 'commandPackage',
386
+ surface: { kind: 'aw', name: 'managed-commands' }, scopes: [pkg.scope], states: ['present'],
387
+ refreshKeys: [...pkg.refreshKeys], producer: { kind: 'package', packageKey: pkg.key }, packageKey: pkg.key,
388
+ }));
389
+ for (const set of managed.sets) {
390
+ if (set.mode !== 'dynamic')
391
+ continue;
392
+ const base = { id: `command-set.${set.set}`, target: 'aw.command-set', kind: 'commandSet', states: ['present', 'disabled'], refreshKeys: [...set.refreshKeys], producer: set.producer, set: set.set };
393
+ const producer = set.producer;
394
+ if (producer.kind === 'package') {
395
+ const pkg = managed.packages.find((item) => item.key === producer.packageKey);
396
+ result.push({ ...base, surface: { kind: 'aw', name: 'managed-commands' }, scopes: [pkg.scope], packageKey: pkg.key });
397
+ }
398
+ else if (set.producer.kind === 'static-skill') {
399
+ result.push({ ...base, platform: set.producer.platform, surface: { kind: 'platform', platform: set.producer.platform }, scopes: ['user'] });
400
+ }
401
+ else {
402
+ const target = manifest.artifacts.targets.find((item) => item.id === producer.artifactId);
403
+ result.push({ ...base, platform: target.platform, surface: { kind: 'platform', platform: target.platform }, scopes: target.delivery?.mode === 'codex-native' ? ['user'] : [...target.scopes], ...(target.delivery ? { delivery: target.delivery } : {}) });
404
+ }
405
+ }
406
+ return result;
407
+ }
408
+ /** Checks entry path chains after a producer candidate is complete. */
409
+ export async function validateCommandEntryFiles(resourceRoot, entries) {
410
+ const root = await fs.lstat(resourceRoot);
411
+ if (root.isSymbolicLink() || !root.isDirectory())
412
+ throw new Error('Invalid command resource root: expected a regular directory.');
413
+ for (const entry of entries) {
414
+ normalizeCommandDefinition(entry);
415
+ let current = resourceRoot;
416
+ const segments = entry.entry.split('/');
417
+ for (const [index, segment] of segments.entries()) {
418
+ current = path.join(current, segment);
419
+ const stat = await fs.lstat(current).catch(() => undefined);
420
+ if (!stat || stat.isSymbolicLink() || (index === segments.length - 1 ? !stat.isFile() : !stat.isDirectory()))
421
+ throw new Error('Invalid command entry: expected a producer-owned regular file without links.');
422
+ }
423
+ }
424
+ }
425
+ /** Validates the package input closure without following dependency or author-only trees. */
426
+ export async function validateManagedCommandSources(pluginRoot, manifest) {
427
+ for (const set of manifest.managedCommands?.sets ?? []) {
428
+ if (set.producer.kind !== 'static-skill')
429
+ continue;
430
+ const producer = set.producer;
431
+ const sourceRoot = path.join(pluginRoot, manifest.skills.slice(2), producer.skillKey);
432
+ const bundleRoot = path.join(pluginRoot, producer.platform, manifest.skills.slice(2), producer.skillKey);
433
+ const root = await fs.pathExists(sourceRoot) ? sourceRoot : bundleRoot;
434
+ const stat = await fs.lstat(path.join(root, 'SKILL.md')).catch(() => undefined);
435
+ if (!stat || stat.isSymbolicLink() || !stat.isFile())
436
+ throw new Error(`Invalid managed command set ${set.set}: static Skill declaration key does not exist.`);
437
+ }
438
+ for (const pkg of manifest.managedCommands?.packages ?? []) {
439
+ let current = pluginRoot;
440
+ for (const segment of pkg.sourceDir.slice(2).split('/')) {
441
+ current = path.join(current, segment);
442
+ const stat = await fs.lstat(current).catch(() => undefined);
443
+ if (!stat || stat.isSymbolicLink() || !stat.isDirectory())
444
+ throw new Error(`Invalid managed command package ${pkg.key}: sourceDir must be an ordinary directory without links.`);
445
+ }
446
+ const walk = async (directory, relative = '') => {
447
+ for (const entry of await fs.readdir(directory, { withFileTypes: true })) {
448
+ const relativePath = relative ? `${relative}/${entry.name}` : entry.name;
449
+ if (PACKAGE_PROTECTED_NAMES.has(entry.name) || isSkillAuthorOnlyPath(relativePath)) {
450
+ throw new Error(`Invalid managed command package ${pkg.key}: protected or author-only content.`);
451
+ }
452
+ if (entry.isSymbolicLink() || (!entry.isDirectory() && !entry.isFile()))
453
+ throw new Error(`Invalid managed command package ${pkg.key}: only ordinary files and directories are supported.`);
454
+ if (entry.isDirectory())
455
+ await walk(path.join(directory, entry.name), relativePath);
456
+ }
457
+ };
458
+ await walk(current);
459
+ const entries = (manifest.managedCommands?.sets ?? []).flatMap((set) => set.producer.kind === 'package' && set.producer.packageKey === pkg.key && set.mode === 'static' ? set.entries : []);
460
+ await validateCommandEntryFiles(current, entries);
461
+ }
462
+ }
463
+ //# sourceMappingURL=managed-commands.js.map