@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,594 @@
|
|
|
1
|
+
import { ENTITLEMENT_LEASE_ALGORITHM, ENTITLEMENT_LEASE_PURPOSE, CommercialArtifactError, createInstallationPossessionProof, readVerifiedActiveCommercialArtifact, resolveManagedInstallationLayout, retainVerifiedEntitlementRevocationEpoch, RELEASE_MANIFEST_PURPOSE, verifyEntitlementLease, verifyInstallationPossessionProof, verifyOfflineActivationChallenge, verifyOfflineActivationResponse, } from '@aopslabs/artifact-trust-contracts';
|
|
2
|
+
import { randomBytes, randomUUID } from 'node:crypto';
|
|
3
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, writeFileSync, } from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
const CONFIG_OBJECT_TYPE_V1 = 'aops-commercial-admission-config-v1';
|
|
8
|
+
const CONFIG_OBJECT_TYPE_V2 = 'aops-commercial-admission-config-v2';
|
|
9
|
+
const CONFIG_KEYS_V1 = Object.freeze([
|
|
10
|
+
'domain',
|
|
11
|
+
'entitlementEpoch',
|
|
12
|
+
'extensionNamespace',
|
|
13
|
+
'hostInstanceId',
|
|
14
|
+
'installationId',
|
|
15
|
+
'issuer',
|
|
16
|
+
'leaseFile',
|
|
17
|
+
'leaseMode',
|
|
18
|
+
'leaseTrustBundle',
|
|
19
|
+
'minimumRevocationEpoch',
|
|
20
|
+
'objectType',
|
|
21
|
+
'productId',
|
|
22
|
+
'releaseTrustBundle',
|
|
23
|
+
'schemaVersion',
|
|
24
|
+
'storeRoot',
|
|
25
|
+
'tenantId',
|
|
26
|
+
'target',
|
|
27
|
+
]);
|
|
28
|
+
const CONFIG_KEYS_V2 = Object.freeze([
|
|
29
|
+
...CONFIG_KEYS_V1,
|
|
30
|
+
'activationChallengeDigest',
|
|
31
|
+
'installationKeyThumbprint',
|
|
32
|
+
'installationPrivateKeyFile',
|
|
33
|
+
]);
|
|
34
|
+
const SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
35
|
+
function commercialIntegrityReadOptions(storeRoot) {
|
|
36
|
+
const layout = resolveManagedInstallationLayout();
|
|
37
|
+
if (path.resolve(storeRoot) !== path.resolve(layout.storeRoot)) {
|
|
38
|
+
return Object.freeze({ verificationMode: 'full' });
|
|
39
|
+
}
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
integrityStateRoot: layout.stateRoot,
|
|
42
|
+
verificationMode: 'routine',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function isRecord(value) {
|
|
46
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
47
|
+
}
|
|
48
|
+
function exactKeys(value, expected, reason) {
|
|
49
|
+
const actual = Object.keys(value).sort();
|
|
50
|
+
const sorted = [...expected].sort();
|
|
51
|
+
if (actual.length !== sorted.length || actual.some((key, index) => key !== sorted[index])) {
|
|
52
|
+
throw new Error(reason);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function configPath(options) {
|
|
56
|
+
return path.resolve(options.config?.trim() ||
|
|
57
|
+
process.env.AOPS_COMMERCIAL_ADMISSION_CONFIG?.trim() ||
|
|
58
|
+
path.join(os.homedir(), '.aops', 'commercial-admission.json'));
|
|
59
|
+
}
|
|
60
|
+
function safeFile(filePath, reason) {
|
|
61
|
+
if (!path.isAbsolute(filePath))
|
|
62
|
+
throw new Error(reason);
|
|
63
|
+
const stat = lstatSync(filePath);
|
|
64
|
+
if (stat.isSymbolicLink() || !stat.isFile())
|
|
65
|
+
throw new Error(reason);
|
|
66
|
+
return realpathSync.native(filePath);
|
|
67
|
+
}
|
|
68
|
+
function parseJsonFile(filePath, reason) {
|
|
69
|
+
try {
|
|
70
|
+
return JSON.parse(readFileSync(safeFile(filePath, reason), 'utf8'));
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error instanceof Error && error.message === reason)
|
|
74
|
+
throw error;
|
|
75
|
+
throw new Error(reason);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export function parseReleaseTrustBundleFile(filePath) {
|
|
79
|
+
const value = parseJsonFile(filePath, 'release_trust_bundle_invalid');
|
|
80
|
+
if (!isRecord(value))
|
|
81
|
+
throw new Error('release_trust_bundle_invalid');
|
|
82
|
+
exactKeys(value, ['schemaVersion', 'objectType', 'purpose', 'keys'], 'release_trust_bundle_invalid');
|
|
83
|
+
if (value.schemaVersion !== 1 ||
|
|
84
|
+
value.objectType !== 'aops-release-trust-bundle-v1' ||
|
|
85
|
+
value.purpose !== RELEASE_MANIFEST_PURPOSE ||
|
|
86
|
+
!Array.isArray(value.keys) ||
|
|
87
|
+
value.keys.length === 0)
|
|
88
|
+
throw new Error('release_trust_bundle_invalid');
|
|
89
|
+
return Object.freeze(value.keys.map((candidate) => {
|
|
90
|
+
if (!isRecord(candidate))
|
|
91
|
+
throw new Error('release_trust_bundle_invalid');
|
|
92
|
+
const actualKeys = Object.keys(candidate).sort();
|
|
93
|
+
const legacyKeys = ['alg', 'kid', 'publicKeyPem', 'purpose'];
|
|
94
|
+
const rotatingKeys = [
|
|
95
|
+
'alg', 'kid', 'notAfter', 'notBefore', 'publicKeyPem', 'purpose', 'status',
|
|
96
|
+
];
|
|
97
|
+
const rotating = actualKeys.length === rotatingKeys.length &&
|
|
98
|
+
actualKeys.every((key, index) => key === rotatingKeys[index]);
|
|
99
|
+
if (!rotating &&
|
|
100
|
+
(actualKeys.length !== legacyKeys.length ||
|
|
101
|
+
actualKeys.some((key, index) => key !== legacyKeys[index])))
|
|
102
|
+
throw new Error('release_trust_bundle_invalid');
|
|
103
|
+
if (candidate.alg !== 'EdDSA' ||
|
|
104
|
+
candidate.purpose !== RELEASE_MANIFEST_PURPOSE ||
|
|
105
|
+
typeof candidate.kid !== 'string' ||
|
|
106
|
+
typeof candidate.publicKeyPem !== 'string' ||
|
|
107
|
+
(rotating &&
|
|
108
|
+
!['active', 'verify-only', 'retired', 'revoked'].includes(String(candidate.status))) ||
|
|
109
|
+
(rotating &&
|
|
110
|
+
candidate.notBefore !== null &&
|
|
111
|
+
typeof candidate.notBefore !== 'string') ||
|
|
112
|
+
(rotating &&
|
|
113
|
+
candidate.notAfter !== null &&
|
|
114
|
+
typeof candidate.notAfter !== 'string'))
|
|
115
|
+
throw new Error('release_trust_bundle_invalid');
|
|
116
|
+
if (candidate.kid.toLowerCase().includes('test-ephemeral')) {
|
|
117
|
+
throw new Error('release_trust_test_key_refused');
|
|
118
|
+
}
|
|
119
|
+
return Object.freeze({
|
|
120
|
+
alg: 'EdDSA',
|
|
121
|
+
purpose: RELEASE_MANIFEST_PURPOSE,
|
|
122
|
+
kid: candidate.kid,
|
|
123
|
+
publicKeyPem: candidate.publicKeyPem,
|
|
124
|
+
...(rotating ? {
|
|
125
|
+
status: candidate.status,
|
|
126
|
+
...(candidate.notBefore === null
|
|
127
|
+
? {}
|
|
128
|
+
: { notBefore: candidate.notBefore }),
|
|
129
|
+
...(candidate.notAfter === null
|
|
130
|
+
? {}
|
|
131
|
+
: { notAfter: candidate.notAfter }),
|
|
132
|
+
} : {}),
|
|
133
|
+
});
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
export function parseLeaseTrustBundleFile(filePath) {
|
|
137
|
+
const value = parseJsonFile(filePath, 'lease_trust_bundle_invalid');
|
|
138
|
+
if (!isRecord(value))
|
|
139
|
+
throw new Error('lease_trust_bundle_invalid');
|
|
140
|
+
exactKeys(value, ['schemaVersion', 'objectType', 'purpose', 'keys'], 'lease_trust_bundle_invalid');
|
|
141
|
+
if (value.schemaVersion !== 1 ||
|
|
142
|
+
value.objectType !== 'aops-entitlement-lease-trust-bundle-v1' ||
|
|
143
|
+
value.purpose !== ENTITLEMENT_LEASE_PURPOSE ||
|
|
144
|
+
!Array.isArray(value.keys) ||
|
|
145
|
+
value.keys.length === 0)
|
|
146
|
+
throw new Error('lease_trust_bundle_invalid');
|
|
147
|
+
return Object.freeze(value.keys.map((candidate) => {
|
|
148
|
+
if (!isRecord(candidate))
|
|
149
|
+
throw new Error('lease_trust_bundle_invalid');
|
|
150
|
+
exactKeys(candidate, ['alg', 'kid', 'notAfter', 'notBefore', 'publicKeyPem', 'purpose', 'status'], 'lease_trust_bundle_invalid');
|
|
151
|
+
if (candidate.alg !== ENTITLEMENT_LEASE_ALGORITHM ||
|
|
152
|
+
candidate.purpose !== ENTITLEMENT_LEASE_PURPOSE ||
|
|
153
|
+
typeof candidate.kid !== 'string' ||
|
|
154
|
+
typeof candidate.publicKeyPem !== 'string' ||
|
|
155
|
+
(candidate.status !== 'active' && candidate.status !== 'verify-only') ||
|
|
156
|
+
(candidate.notBefore !== null && typeof candidate.notBefore !== 'string') ||
|
|
157
|
+
(candidate.notAfter !== null && typeof candidate.notAfter !== 'string'))
|
|
158
|
+
throw new Error('lease_trust_bundle_invalid');
|
|
159
|
+
return Object.freeze({
|
|
160
|
+
alg: ENTITLEMENT_LEASE_ALGORITHM,
|
|
161
|
+
purpose: ENTITLEMENT_LEASE_PURPOSE,
|
|
162
|
+
kid: candidate.kid,
|
|
163
|
+
publicKeyPem: candidate.publicKeyPem,
|
|
164
|
+
status: candidate.status,
|
|
165
|
+
...(candidate.notBefore === null ? {} : { notBefore: candidate.notBefore }),
|
|
166
|
+
...(candidate.notAfter === null ? {} : { notAfter: candidate.notAfter }),
|
|
167
|
+
});
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
function integer(value, fallback) {
|
|
171
|
+
if (value === undefined)
|
|
172
|
+
return fallback;
|
|
173
|
+
const parsed = Number(value);
|
|
174
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0)
|
|
175
|
+
throw new Error('commercial_license_epoch_invalid');
|
|
176
|
+
return parsed;
|
|
177
|
+
}
|
|
178
|
+
function digestHex(value, reason) {
|
|
179
|
+
const normalized = value.startsWith('sha256:') ? value.slice('sha256:'.length) : value;
|
|
180
|
+
if (!SHA256_HEX.test(normalized))
|
|
181
|
+
throw new Error(reason);
|
|
182
|
+
return normalized;
|
|
183
|
+
}
|
|
184
|
+
function parseConfig(value) {
|
|
185
|
+
if (!isRecord(value))
|
|
186
|
+
throw new Error('commercial_admission_config_invalid');
|
|
187
|
+
if ((value.schemaVersion !== 1 && value.schemaVersion !== 2) ||
|
|
188
|
+
value.objectType !== (value.schemaVersion === 1 ? CONFIG_OBJECT_TYPE_V1 : CONFIG_OBJECT_TYPE_V2)) {
|
|
189
|
+
throw new Error('commercial_admission_config_invalid');
|
|
190
|
+
}
|
|
191
|
+
exactKeys(value, value.schemaVersion === 1 ? CONFIG_KEYS_V1 : CONFIG_KEYS_V2, 'commercial_admission_config_invalid');
|
|
192
|
+
return value;
|
|
193
|
+
}
|
|
194
|
+
function verifyConfig(config, now = new Date(), activationChallengeToken, requireRetainedRevocationEpoch = false, persistRevocationEpoch = false) {
|
|
195
|
+
const requiredText = (key) => {
|
|
196
|
+
const value = config[key];
|
|
197
|
+
if (typeof value !== 'string' || value !== value.trim() || value.length === 0) {
|
|
198
|
+
throw new Error('commercial_admission_config_invalid');
|
|
199
|
+
}
|
|
200
|
+
return value;
|
|
201
|
+
};
|
|
202
|
+
const domain = requiredText('domain').toLowerCase();
|
|
203
|
+
const productId = requiredText('productId');
|
|
204
|
+
const selectedStoreRoot = requiredText('storeRoot');
|
|
205
|
+
const active = readVerifiedActiveCommercialArtifact({
|
|
206
|
+
storeRoot: selectedStoreRoot,
|
|
207
|
+
...commercialIntegrityReadOptions(selectedStoreRoot),
|
|
208
|
+
trustKeys: parseReleaseTrustBundleFile(requiredText('releaseTrustBundle')),
|
|
209
|
+
now,
|
|
210
|
+
expectedIdentity: {
|
|
211
|
+
installationId: requiredText('installationId'),
|
|
212
|
+
hostInstanceId: requiredText('hostInstanceId'),
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
if (!active)
|
|
216
|
+
throw new Error('commercial_artifact_not_active');
|
|
217
|
+
const extension = active.manifest.envelope.payload.extensions[requiredText('extensionNamespace')];
|
|
218
|
+
if (!isRecord(extension) ||
|
|
219
|
+
extension.releaseTier !== 'commercial' ||
|
|
220
|
+
!Array.isArray(extension.productIds) ||
|
|
221
|
+
!extension.productIds.includes(productId) ||
|
|
222
|
+
!Array.isArray(extension.operationFeatures))
|
|
223
|
+
throw new Error('commercial_release_extension_invalid');
|
|
224
|
+
const token = readFileSync(safeFile(requiredText('leaseFile'), 'entitlement_lease_file_invalid'), 'utf8').trim();
|
|
225
|
+
const entitlementEpoch = config.entitlementEpoch;
|
|
226
|
+
const minimumRevocationEpoch = config.minimumRevocationEpoch;
|
|
227
|
+
const leaseMode = config.leaseMode;
|
|
228
|
+
const schemaVersion = config.schemaVersion;
|
|
229
|
+
if ((schemaVersion !== 1 && schemaVersion !== 2) ||
|
|
230
|
+
(entitlementEpoch !== null && (!Number.isSafeInteger(entitlementEpoch) || Number(entitlementEpoch) < 0)) ||
|
|
231
|
+
(minimumRevocationEpoch !== null &&
|
|
232
|
+
(!Number.isSafeInteger(minimumRevocationEpoch) || Number(minimumRevocationEpoch) < 0)) ||
|
|
233
|
+
(leaseMode !== null && leaseMode !== 'online' && leaseMode !== 'offline'))
|
|
234
|
+
throw new Error('commercial_admission_config_invalid');
|
|
235
|
+
if (schemaVersion === 2 && leaseMode !== 'offline') {
|
|
236
|
+
throw new Error('commercial_admission_config_invalid');
|
|
237
|
+
}
|
|
238
|
+
const activationChallengeDigest = schemaVersion === 2
|
|
239
|
+
? requiredText('activationChallengeDigest')
|
|
240
|
+
: null;
|
|
241
|
+
const installationKeyThumbprint = schemaVersion === 2
|
|
242
|
+
? requiredText('installationKeyThumbprint')
|
|
243
|
+
: null;
|
|
244
|
+
const installationPrivateKeyFile = schemaVersion === 2
|
|
245
|
+
? requiredText('installationPrivateKeyFile')
|
|
246
|
+
: null;
|
|
247
|
+
if ((activationChallengeDigest !== null && !SHA256_HEX.test(activationChallengeDigest)) ||
|
|
248
|
+
(installationKeyThumbprint !== null && !SHA256_HEX.test(installationKeyThumbprint)))
|
|
249
|
+
throw new Error('commercial_admission_config_invalid');
|
|
250
|
+
const lease = verifyEntitlementLease({
|
|
251
|
+
token,
|
|
252
|
+
trustKeys: parseLeaseTrustBundleFile(requiredText('leaseTrustBundle')),
|
|
253
|
+
issuer: requiredText('issuer'),
|
|
254
|
+
now,
|
|
255
|
+
expected: {
|
|
256
|
+
tenantId: requiredText('tenantId'),
|
|
257
|
+
installationId: requiredText('installationId'),
|
|
258
|
+
productId,
|
|
259
|
+
domain,
|
|
260
|
+
manifestDigest: active.manifest.manifestDigest,
|
|
261
|
+
closureDigest: active.receipt.closureDigest,
|
|
262
|
+
...(entitlementEpoch === null ? {} : { entitlementEpoch: Number(entitlementEpoch) }),
|
|
263
|
+
...(minimumRevocationEpoch === null ? {} : {
|
|
264
|
+
minimumRevocationEpoch: Number(minimumRevocationEpoch),
|
|
265
|
+
}),
|
|
266
|
+
...(leaseMode === null ? {} : { leaseMode }),
|
|
267
|
+
...(installationKeyThumbprint === null ? {} : { installationKeyThumbprint }),
|
|
268
|
+
...(activationChallengeDigest === null ? {} : { activationChallengeDigest }),
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
retainVerifiedEntitlementRevocationEpoch({
|
|
272
|
+
storeRoot: requiredText('storeRoot'),
|
|
273
|
+
hostInstanceId: requiredText('hostInstanceId'),
|
|
274
|
+
lease,
|
|
275
|
+
requireExisting: requireRetainedRevocationEpoch,
|
|
276
|
+
persist: persistRevocationEpoch,
|
|
277
|
+
});
|
|
278
|
+
if (schemaVersion === 2) {
|
|
279
|
+
if (lease.claims.schemaVersion !== 2 ||
|
|
280
|
+
activationChallengeDigest === null ||
|
|
281
|
+
installationKeyThumbprint === null ||
|
|
282
|
+
installationPrivateKeyFile === null)
|
|
283
|
+
throw new Error('commercial_installation_possession_invalid');
|
|
284
|
+
if (activationChallengeToken !== undefined) {
|
|
285
|
+
const activation = verifyOfflineActivationResponse({
|
|
286
|
+
response: {
|
|
287
|
+
schemaVersion: 1,
|
|
288
|
+
objectType: 'aops-offline-activation-response',
|
|
289
|
+
challengeDigest: activationChallengeDigest,
|
|
290
|
+
lease: token,
|
|
291
|
+
},
|
|
292
|
+
challengeToken: activationChallengeToken,
|
|
293
|
+
leaseTrustKeys: parseLeaseTrustBundleFile(requiredText('leaseTrustBundle')),
|
|
294
|
+
issuer: requiredText('issuer'),
|
|
295
|
+
now,
|
|
296
|
+
expected: {
|
|
297
|
+
domain,
|
|
298
|
+
...(entitlementEpoch === null ? {} : {
|
|
299
|
+
entitlementEpoch: Number(entitlementEpoch),
|
|
300
|
+
}),
|
|
301
|
+
...(minimumRevocationEpoch === null ? {} : {
|
|
302
|
+
minimumRevocationEpoch: Number(minimumRevocationEpoch),
|
|
303
|
+
}),
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
if (activation.challenge.payload.hostInstanceId !== requiredText('hostInstanceId')) {
|
|
307
|
+
throw new Error('commercial_installation_possession_invalid');
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const nonce = randomBytes(32).toString('base64url');
|
|
311
|
+
const iat = Math.floor(now.getTime() / 1000);
|
|
312
|
+
const privateKeyPem = readFileSync(safeFile(installationPrivateKeyFile, 'commercial_installation_private_key_invalid'), 'utf8');
|
|
313
|
+
const receiptBinding = {
|
|
314
|
+
receiptId: active.receipt.receiptId,
|
|
315
|
+
releaseId: active.receipt.releaseId,
|
|
316
|
+
manifestDigest: digestHex(active.receipt.manifestDigest, 'commercial_installation_possession_invalid'),
|
|
317
|
+
closureDigest: digestHex(active.receipt.closureDigest, 'commercial_installation_possession_invalid'),
|
|
318
|
+
};
|
|
319
|
+
const proof = createInstallationPossessionProof({
|
|
320
|
+
privateKeyPem,
|
|
321
|
+
proofId: randomUUID(),
|
|
322
|
+
nonce,
|
|
323
|
+
installationId: requiredText('installationId'),
|
|
324
|
+
hostInstanceId: requiredText('hostInstanceId'),
|
|
325
|
+
leaseToken: token,
|
|
326
|
+
activationChallengeDigest,
|
|
327
|
+
receiptBinding,
|
|
328
|
+
iat,
|
|
329
|
+
exp: iat + 2 * 60,
|
|
330
|
+
});
|
|
331
|
+
verifyInstallationPossessionProof({
|
|
332
|
+
token: proof.token,
|
|
333
|
+
now,
|
|
334
|
+
expected: {
|
|
335
|
+
nonce,
|
|
336
|
+
installationId: requiredText('installationId'),
|
|
337
|
+
hostInstanceId: requiredText('hostInstanceId'),
|
|
338
|
+
installationKeyThumbprint,
|
|
339
|
+
leaseToken: token,
|
|
340
|
+
activationChallengeDigest,
|
|
341
|
+
...receiptBinding,
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
return {
|
|
346
|
+
active,
|
|
347
|
+
lease,
|
|
348
|
+
domain,
|
|
349
|
+
productId,
|
|
350
|
+
target: requiredText('target'),
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function print(value) {
|
|
354
|
+
console.log(JSON.stringify(value, null, 2));
|
|
355
|
+
}
|
|
356
|
+
function licenseDiagnostic(error) {
|
|
357
|
+
if (error instanceof CommercialArtifactError) {
|
|
358
|
+
return Object.freeze({
|
|
359
|
+
stage: error.stage,
|
|
360
|
+
decision: error.decision,
|
|
361
|
+
reason: error.reason,
|
|
362
|
+
next: error.decision === 'quarantine'
|
|
363
|
+
? 'Reinstall a trusted artifact candidate, then run aops license status.'
|
|
364
|
+
: 'Repair the verified artifact evidence, then run aops license status.',
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
368
|
+
if (reason.startsWith('lease-') || reason.includes('lease_') || reason.includes('entitlement_')) {
|
|
369
|
+
return Object.freeze({
|
|
370
|
+
stage: 'entitlement-lease',
|
|
371
|
+
decision: 'deny',
|
|
372
|
+
reason,
|
|
373
|
+
next: 'Obtain a current lease for the selected target, then run aops license activate.',
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
if (reason.includes('trust_bundle')) {
|
|
377
|
+
return Object.freeze({
|
|
378
|
+
stage: 'trust-bundle',
|
|
379
|
+
decision: 'deny',
|
|
380
|
+
reason,
|
|
381
|
+
next: 'Repair the host-pinned trust bundle, then run aops license status.',
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
if (reason.includes('artifact') || reason.includes('release_extension')) {
|
|
385
|
+
return Object.freeze({
|
|
386
|
+
stage: 'commercial-artifact',
|
|
387
|
+
decision: 'deny',
|
|
388
|
+
reason,
|
|
389
|
+
next: 'Install a verified compatible artifact, then run aops license status.',
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
return Object.freeze({
|
|
393
|
+
stage: 'configuration',
|
|
394
|
+
decision: 'deny',
|
|
395
|
+
reason,
|
|
396
|
+
next: 'Run aops license activate --help and repair the user-owned admission config.',
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
function handleError(error) {
|
|
400
|
+
const diagnostic = licenseDiagnostic(error);
|
|
401
|
+
print({
|
|
402
|
+
ok: false,
|
|
403
|
+
schemaVersion: 1,
|
|
404
|
+
contract: 'aops-license-error-v1',
|
|
405
|
+
action: 'commercial-license',
|
|
406
|
+
stage: diagnostic.stage,
|
|
407
|
+
decision: diagnostic.decision,
|
|
408
|
+
reason: diagnostic.reason,
|
|
409
|
+
next: diagnostic.next,
|
|
410
|
+
});
|
|
411
|
+
process.exitCode = 1;
|
|
412
|
+
}
|
|
413
|
+
export function runCommercialLicenseStatus(options = {}) {
|
|
414
|
+
const target = configPath(options);
|
|
415
|
+
if (!existsSync(target)) {
|
|
416
|
+
print({
|
|
417
|
+
ok: true,
|
|
418
|
+
schemaVersion: 1,
|
|
419
|
+
contract: 'aops-license-status-v1',
|
|
420
|
+
action: 'commercial-license.status',
|
|
421
|
+
state: 'community-free',
|
|
422
|
+
commercialConfigured: false,
|
|
423
|
+
config: target,
|
|
424
|
+
next: 'aops license activate --help',
|
|
425
|
+
});
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
const verified = verifyConfig(parseConfig(parseJsonFile(target, 'commercial_admission_config_invalid')), new Date(), undefined, true, false);
|
|
430
|
+
print({
|
|
431
|
+
ok: true,
|
|
432
|
+
schemaVersion: 1,
|
|
433
|
+
contract: 'aops-license-status-v1',
|
|
434
|
+
action: 'commercial-license.status',
|
|
435
|
+
state: verified.lease.mode,
|
|
436
|
+
commercialConfigured: true,
|
|
437
|
+
config: target,
|
|
438
|
+
target: verified.target,
|
|
439
|
+
domain: verified.domain,
|
|
440
|
+
productId: verified.productId,
|
|
441
|
+
releaseId: verified.active.receipt.releaseId,
|
|
442
|
+
manifestDigest: verified.active.manifest.manifestDigest,
|
|
443
|
+
leaseId: verified.lease.claims.jti,
|
|
444
|
+
entitlementEpoch: verified.lease.claims.entitlementEpoch,
|
|
445
|
+
revocationEpoch: verified.lease.claims.revocationEpoch,
|
|
446
|
+
refreshDue: verified.lease.refreshDue,
|
|
447
|
+
refreshAfter: verified.lease.claims.refreshAfter,
|
|
448
|
+
fullUntil: verified.lease.claims.fullUntil,
|
|
449
|
+
expiresAt: verified.lease.claims.exp,
|
|
450
|
+
next: verified.lease.mode === 'degraded' || verified.lease.refreshDue
|
|
451
|
+
? 'Obtain a refreshed lease for the selected target.'
|
|
452
|
+
: 'No action required.',
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
catch (error) {
|
|
456
|
+
handleError(error);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
export function runCommercialLicenseActivate(options) {
|
|
460
|
+
try {
|
|
461
|
+
const target = configPath(options);
|
|
462
|
+
const existingConfig = existsSync(target);
|
|
463
|
+
if (existingConfig) {
|
|
464
|
+
const stat = lstatSync(target);
|
|
465
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
466
|
+
throw new Error('commercial_admission_config_unsafe');
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
const domain = options.domain.trim().toLowerCase();
|
|
470
|
+
const leaseMode = options.leaseMode ?? null;
|
|
471
|
+
const offline = leaseMode === 'offline';
|
|
472
|
+
if ((offline && (!options.activationChallengeFile || !options.installationPrivateKeyFile)) ||
|
|
473
|
+
(!offline && (options.activationChallengeFile || options.installationPrivateKeyFile)))
|
|
474
|
+
throw new Error('commercial_offline_activation_evidence_required');
|
|
475
|
+
const tenantId = options.tenantId.trim();
|
|
476
|
+
const installationId = options.installationId.trim();
|
|
477
|
+
const hostInstanceId = options.hostInstanceId.trim();
|
|
478
|
+
const productId = options.productId.trim();
|
|
479
|
+
let activationChallengeToken;
|
|
480
|
+
let activationChallengeDigest;
|
|
481
|
+
let installationKeyThumbprint;
|
|
482
|
+
let installationPrivateKeyFile;
|
|
483
|
+
if (offline) {
|
|
484
|
+
const challengeFile = safeFile(path.resolve(options.activationChallengeFile), 'offline_activation_challenge_file_invalid');
|
|
485
|
+
activationChallengeToken = readFileSync(challengeFile, 'utf8').trim();
|
|
486
|
+
const challenge = verifyOfflineActivationChallenge({
|
|
487
|
+
token: activationChallengeToken,
|
|
488
|
+
expected: { tenantId, productId },
|
|
489
|
+
});
|
|
490
|
+
if (challenge.payload.installationId !== installationId ||
|
|
491
|
+
challenge.payload.hostInstanceId !== hostInstanceId)
|
|
492
|
+
throw new Error('commercial_installation_possession_invalid');
|
|
493
|
+
activationChallengeDigest = challenge.challengeDigest;
|
|
494
|
+
installationKeyThumbprint = challenge.installationKeyThumbprint;
|
|
495
|
+
installationPrivateKeyFile = safeFile(path.resolve(options.installationPrivateKeyFile), 'commercial_installation_private_key_invalid');
|
|
496
|
+
}
|
|
497
|
+
const commonConfig = Object.freeze({
|
|
498
|
+
target: options.target.trim(),
|
|
499
|
+
storeRoot: path.resolve(options.storeRoot),
|
|
500
|
+
releaseTrustBundle: safeFile(path.resolve(options.releaseTrustBundle), 'release_trust_bundle_invalid'),
|
|
501
|
+
leaseFile: safeFile(path.resolve(options.leaseFile), 'entitlement_lease_file_invalid'),
|
|
502
|
+
leaseTrustBundle: safeFile(path.resolve(options.leaseTrustBundle), 'lease_trust_bundle_invalid'),
|
|
503
|
+
issuer: options.issuer.trim(),
|
|
504
|
+
tenantId,
|
|
505
|
+
installationId,
|
|
506
|
+
hostInstanceId,
|
|
507
|
+
productId,
|
|
508
|
+
domain,
|
|
509
|
+
extensionNamespace: options.extensionNamespace?.trim() || `com.aopslab.${domain}`,
|
|
510
|
+
entitlementEpoch: integer(options.entitlementEpoch, null),
|
|
511
|
+
minimumRevocationEpoch: integer(options.minimumRevocationEpoch, 0),
|
|
512
|
+
leaseMode,
|
|
513
|
+
});
|
|
514
|
+
const config = Object.freeze(offline
|
|
515
|
+
? {
|
|
516
|
+
schemaVersion: 2,
|
|
517
|
+
objectType: CONFIG_OBJECT_TYPE_V2,
|
|
518
|
+
...commonConfig,
|
|
519
|
+
activationChallengeDigest,
|
|
520
|
+
installationKeyThumbprint,
|
|
521
|
+
installationPrivateKeyFile,
|
|
522
|
+
}
|
|
523
|
+
: {
|
|
524
|
+
schemaVersion: 1,
|
|
525
|
+
objectType: CONFIG_OBJECT_TYPE_V1,
|
|
526
|
+
...commonConfig,
|
|
527
|
+
});
|
|
528
|
+
const verified = verifyConfig(config, new Date(), activationChallengeToken, existingConfig, true);
|
|
529
|
+
const parent = path.dirname(target);
|
|
530
|
+
mkdirSync(parent, { recursive: true, mode: 0o700 });
|
|
531
|
+
const temporary = path.join(parent, `.commercial-admission-${randomUUID()}.tmp`);
|
|
532
|
+
writeFileSync(temporary, `${JSON.stringify(config, null, 2)}\n`, {
|
|
533
|
+
flag: 'wx',
|
|
534
|
+
mode: 0o600,
|
|
535
|
+
});
|
|
536
|
+
renameSync(temporary, target);
|
|
537
|
+
chmodSync(target, 0o600);
|
|
538
|
+
print({
|
|
539
|
+
ok: true,
|
|
540
|
+
schemaVersion: 1,
|
|
541
|
+
contract: 'aops-license-activate-v1',
|
|
542
|
+
action: 'commercial-license.activate',
|
|
543
|
+
decision: 'configured',
|
|
544
|
+
config: target,
|
|
545
|
+
target: verified.target,
|
|
546
|
+
domain: verified.domain,
|
|
547
|
+
productId: verified.productId,
|
|
548
|
+
state: verified.lease.mode,
|
|
549
|
+
expiresAt: verified.lease.claims.exp,
|
|
550
|
+
next: [
|
|
551
|
+
`Set AOPS_COMMERCIAL_ADMISSION_CONFIG to ${target}`,
|
|
552
|
+
'Restart AOPS Server',
|
|
553
|
+
'Run aops license status',
|
|
554
|
+
],
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
handleError(error);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
export function makeCommercialLicenseCommand() {
|
|
562
|
+
const command = new Command('license')
|
|
563
|
+
.description('Verify and configure commercial domain admission without placing lease tokens in argv');
|
|
564
|
+
command
|
|
565
|
+
.command('status')
|
|
566
|
+
.description('Re-verify configured release, CAS receipt, and entitlement lease')
|
|
567
|
+
.option('--config <path>', 'User-owned commercial admission config')
|
|
568
|
+
.option('--json', 'Print JSON output')
|
|
569
|
+
.action((options) => runCommercialLicenseStatus(options));
|
|
570
|
+
command
|
|
571
|
+
.command('activate')
|
|
572
|
+
.description('Verify external evidence and atomically write a user-owned admission config')
|
|
573
|
+
.option('--config <path>', 'User-owned commercial admission config')
|
|
574
|
+
.requiredOption('--store-root <path>', 'Host-owned commercial artifact store')
|
|
575
|
+
.requiredOption('--target <name>', 'Selected remote licensing target binding')
|
|
576
|
+
.requiredOption('--release-trust-bundle <path>', 'Host-pinned release trust bundle')
|
|
577
|
+
.requiredOption('--lease-file <path>', 'Lease token file; token values are refused on argv')
|
|
578
|
+
.requiredOption('--lease-trust-bundle <path>', 'Host-pinned lease trust bundle')
|
|
579
|
+
.requiredOption('--issuer <issuer>', 'Expected lease issuer')
|
|
580
|
+
.requiredOption('--tenant-id <id>', 'Expected tenant identity')
|
|
581
|
+
.requiredOption('--installation-id <id>', 'Expected installation identity')
|
|
582
|
+
.requiredOption('--host-instance-id <id>', 'Expected host instance identity')
|
|
583
|
+
.requiredOption('--product-id <id>', 'Expected commercial product')
|
|
584
|
+
.requiredOption('--domain <id>', 'Commercial domain id')
|
|
585
|
+
.option('--activation-challenge-file <path>', 'Offline only: installation-signed challenge file used to bind the lease')
|
|
586
|
+
.option('--installation-private-key-file <path>', 'Offline only: user-owned Ed25519 private key file; key bytes never enter argv/config')
|
|
587
|
+
.option('--extension-namespace <namespace>', 'Signed release extension namespace')
|
|
588
|
+
.option('--entitlement-epoch <epoch>', 'Require one exact entitlement epoch')
|
|
589
|
+
.option('--minimum-revocation-epoch <epoch>', 'Minimum accepted revocation epoch', '0')
|
|
590
|
+
.option('--lease-mode <mode>', 'Require online or offline lease mode')
|
|
591
|
+
.option('--json', 'Print JSON output')
|
|
592
|
+
.action((options) => runCommercialLicenseActivate(options));
|
|
593
|
+
return command;
|
|
594
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { runAuthLogin, runAuthLogout, runAuthStatus } from './auth/login.js';
|
|
3
|
+
import { runLegacyAuthExport } from './auth/legacy-export.js';
|
|
4
|
+
const SECRET_ARGV = new Set([
|
|
5
|
+
'--password',
|
|
6
|
+
'--access-token',
|
|
7
|
+
'--refresh-token',
|
|
8
|
+
'--admin-key',
|
|
9
|
+
'--repo-url',
|
|
10
|
+
'--pg-url',
|
|
11
|
+
'--webhook-secret',
|
|
12
|
+
'--jwt-secret',
|
|
13
|
+
'--jwt-enc-secret',
|
|
14
|
+
]);
|
|
15
|
+
export function guardCommunitySecretArgv(argv) {
|
|
16
|
+
for (const token of argv.slice(2)) {
|
|
17
|
+
const flag = token.split('=', 1)[0]?.toLowerCase();
|
|
18
|
+
if (SECRET_ARGV.has(flag)) {
|
|
19
|
+
throw new Error(`community_secret_argv_refused:${flag}:use_interactive_prompt_secure_store_or_environment`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function makeCommunityAuthCommand() {
|
|
24
|
+
const command = new Command('auth').description('Authenticate the CLI to a named remote-session target');
|
|
25
|
+
command.command('login')
|
|
26
|
+
.description('Login with browser approval and store target-bound encrypted tokens')
|
|
27
|
+
.option('--target <name>', 'Named target; default is the active target')
|
|
28
|
+
.option('--api-base-url <url>', 'Server URL; must match --target when both are supplied')
|
|
29
|
+
.option('--timeout-ms <ms>', 'Request timeout', (value) => Number.parseInt(value, 10))
|
|
30
|
+
.option('--yes', 'Non-interactive mode; combine with --browser for browser-approved headless login')
|
|
31
|
+
.option('--browser', 'Open the same-origin approval page and wait headlessly')
|
|
32
|
+
.option('--json', 'Output JSON')
|
|
33
|
+
.action(runAuthLogin);
|
|
34
|
+
command.command('logout')
|
|
35
|
+
.description('Revoke the remote session, then remove encrypted credentials for one target')
|
|
36
|
+
.option('--target <name>', 'Named target; default is the active target')
|
|
37
|
+
.option('--timeout-ms <ms>', 'Request timeout', (value) => Number.parseInt(value, 10))
|
|
38
|
+
.option('--local-only', 'Skip remote revocation and explicitly clear only local credentials')
|
|
39
|
+
.option('--json', 'Output JSON')
|
|
40
|
+
.action(runAuthLogout);
|
|
41
|
+
command.command('status')
|
|
42
|
+
.description('Verify the selected target and current authenticated principal')
|
|
43
|
+
.option('--target <name>', 'Named target; default is the active target')
|
|
44
|
+
.option('--timeout-ms <ms>', 'Request timeout', (value) => Number.parseInt(value, 10))
|
|
45
|
+
.option('--json', 'Output JSON')
|
|
46
|
+
.action(runAuthStatus);
|
|
47
|
+
command.command('legacy-export')
|
|
48
|
+
.description('Export historical local identity/tenant/role metadata without credentials')
|
|
49
|
+
.option('--env-path <path>', 'Private server env containing AOPS_PG_URL')
|
|
50
|
+
.option('--out <path>', 'New private output file; never overwrites an existing file')
|
|
51
|
+
.option('--apply', 'Write --out; without --out the deterministic export is printed')
|
|
52
|
+
.option('--confirm', 'Confirm the explicit historical metadata export')
|
|
53
|
+
.option('--json', 'Return a JSON write receipt when --out is used')
|
|
54
|
+
.addHelpText('after', `
|
|
55
|
+
The old Community AuthV2 tables remain untouched and read-only. Password
|
|
56
|
+
hashes, refresh/session/token rows, verification secrets, and email action
|
|
57
|
+
tokens are never exported. Every migrated user must authenticate again through
|
|
58
|
+
the remote-session device flow.
|
|
59
|
+
`)
|
|
60
|
+
.action(runLegacyAuthExport);
|
|
61
|
+
return command;
|
|
62
|
+
}
|