@appfleet-cli/cli 0.1.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.
@@ -0,0 +1,10 @@
1
+ export type AuditCommandResult = {
2
+ exitCode: number;
3
+ stdout: string;
4
+ stderr: string;
5
+ };
6
+ export declare function runAuditCommand(argv: string[], options?: {
7
+ auditPath?: string;
8
+ syncPath?: string;
9
+ projectRoot?: string;
10
+ }): Promise<AuditCommandResult>;
package/dist/audit.js ADDED
@@ -0,0 +1,85 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { createCloudAuditMetadataFromLocalEvent, createLocalSensitiveAuditEvent, } from "@appfleet/domain";
4
+ export async function runAuditCommand(argv, options = {}) {
5
+ const normalizedArgv = argv[0] === "--" ? argv.slice(1) : argv;
6
+ if (normalizedArgv[0] !== "audit" || normalizedArgv[1] !== "sync") {
7
+ return {
8
+ exitCode: 1,
9
+ stdout: "",
10
+ stderr: "AppFleet audit failed: usage: audit sync [--workspace <workspace-id>] [--json]\n",
11
+ };
12
+ }
13
+ const workspaceId = readFlag(normalizedArgv, "--workspace") ?? "workspace_local";
14
+ const projectRoot = options.projectRoot ?? process.cwd();
15
+ const auditPath = options.auditPath ?? process.env.APPFLEET_AUDIT_PATH ?? join(projectRoot, ".appfleet", "vault-audit.jsonl");
16
+ const syncPath = options.syncPath ?? process.env.APPFLEET_CLOUD_AUDIT_SYNC_PATH ?? join(projectRoot, ".appfleet", "cloud-audit-sync.json");
17
+ const events = await readLocalAuditEvents(auditPath);
18
+ const auditMetadata = events
19
+ .filter((event) => event.workspaceId === workspaceId)
20
+ .map(createCloudAuditMetadataFromLocalEvent);
21
+ const document = {
22
+ type: "cloud_audit_sync_metadata_result",
23
+ version: 1,
24
+ workspaceId,
25
+ auditPath,
26
+ syncPath,
27
+ syncedAuditIds: auditMetadata.map((metadata) => metadata.id),
28
+ auditMetadata,
29
+ localTestPersistenceImplemented: true,
30
+ productionCloudPersistenceImplemented: false,
31
+ acceptsPlaintextSecrets: false,
32
+ includesCommandOutput: false,
33
+ includesEncryptedBlobIds: false,
34
+ providerApiCallsMade: false,
35
+ trustBoundary: [
36
+ "Cloud audit sync stores local audit shadow metadata only.",
37
+ "It omits command output, plaintext secrets, encrypted blob ids, provider payloads, and key material.",
38
+ "It does not call provider APIs.",
39
+ ],
40
+ };
41
+ await mkdir(dirname(syncPath), { recursive: true });
42
+ await writeFile(syncPath, `${JSON.stringify(document, null, 2)}\n`, {
43
+ mode: 0o600,
44
+ });
45
+ return {
46
+ exitCode: 0,
47
+ stdout: normalizedArgv.includes("--json")
48
+ ? `${JSON.stringify(document, null, 2)}\n`
49
+ : `${[
50
+ "AppFleet audit sync: cloud-safe audit metadata persisted locally.",
51
+ `workspaceId=${workspaceId}`,
52
+ `auditPath=${auditPath}`,
53
+ `syncPath=${syncPath}`,
54
+ `auditCount=${auditMetadata.length}`,
55
+ `syncedAuditIds=${auditMetadata.map((metadata) => metadata.id).join(", ")}`,
56
+ "productionCloudPersistence=false",
57
+ "includesCommandOutput=false",
58
+ "includesEncryptedBlobIds=false",
59
+ ].join("\n")}\n`,
60
+ stderr: "",
61
+ };
62
+ }
63
+ async function readLocalAuditEvents(path) {
64
+ try {
65
+ const raw = await readFile(path, "utf8");
66
+ return raw
67
+ .split("\n")
68
+ .map((line) => line.trim())
69
+ .filter(Boolean)
70
+ .map((line) => createLocalSensitiveAuditEvent(JSON.parse(line)));
71
+ }
72
+ catch (error) {
73
+ if (error.code === "ENOENT") {
74
+ return [];
75
+ }
76
+ throw error;
77
+ }
78
+ }
79
+ function readFlag(argv, flag) {
80
+ const index = argv.indexOf(flag);
81
+ if (index === -1) {
82
+ return undefined;
83
+ }
84
+ return argv[index + 1];
85
+ }
@@ -0,0 +1,8 @@
1
+ export type BillingCostCommandResult = {
2
+ exitCode: number;
3
+ stdout: string;
4
+ stderr: string;
5
+ };
6
+ export declare function runBillingCostCommand(argv: string[], options?: {
7
+ now?: () => Date;
8
+ }): BillingCostCommandResult;
@@ -0,0 +1,186 @@
1
+ import { createBillingAccountMetadata, createBillingPaymentFailureDetection, createCostUsageRecordMetadata, createCustomerManagedKmsMetadata, } from "@appfleet/domain";
2
+ export function runBillingCostCommand(argv, options = {}) {
3
+ const now = options.now ?? (() => new Date());
4
+ const normalizedArgv = argv[0] === "--" ? argv.slice(1) : argv;
5
+ const namespace = normalizedArgv[0];
6
+ const action = normalizedArgv[1];
7
+ const outputFormat = normalizedArgv.includes("--json") ? "json" : "human";
8
+ const workspaceId = readFlag(normalizedArgv, "--workspace") ?? "workspace_local";
9
+ const checkedAt = now().toISOString();
10
+ try {
11
+ if (namespace === "billing" && action === "plan") {
12
+ const result = createBillingAccountMetadata({
13
+ type: "billing_account_metadata",
14
+ version: 1,
15
+ workspaceId,
16
+ plan: readBillingPlan(normalizedArgv),
17
+ billingStatus: "provider_credentials_missing",
18
+ createdAt: checkedAt,
19
+ updatedAt: checkedAt,
20
+ productionReady: false,
21
+ productionReadiness: "local_test_metadata_persistence",
22
+ stripeReady: true,
23
+ providerApiCallsMade: false,
24
+ failClosedWithoutProviderCredentials: true,
25
+ containsPaymentMethodDetails: false,
26
+ containsProviderPayload: false,
27
+ containsCredentialValues: false,
28
+ storesDecryptedMaterial: false,
29
+ });
30
+ return ok(outputFormat, result, [
31
+ `AppFleet billing plan: ${result.plan}`,
32
+ `workspace=${result.workspaceId}`,
33
+ `status=${result.billingStatus}`,
34
+ "stripeReady=true",
35
+ "providerApiCallsMade=false",
36
+ ]);
37
+ }
38
+ if (namespace === "billing" && action === "status") {
39
+ const result = {
40
+ type: "billing_status_report",
41
+ version: 1,
42
+ workspaceId,
43
+ checkedAt,
44
+ status: "provider_credentials_missing",
45
+ stripeReady: true,
46
+ canCallStripe: false,
47
+ providerApiCallsMade: false,
48
+ containsPaymentMethodDetails: false,
49
+ containsProviderPayload: false,
50
+ containsCredentialValues: false,
51
+ storesDecryptedMaterial: false,
52
+ };
53
+ return ok(outputFormat, result, [
54
+ "AppFleet billing status.",
55
+ `workspace=${workspaceId}`,
56
+ "status=provider_credentials_missing",
57
+ "canCallStripe=false",
58
+ ]);
59
+ }
60
+ if (namespace === "billing" && action === "failures") {
61
+ const result = createBillingPaymentFailureDetection({
62
+ id: `billing_failure_${workspaceId}_${checkedAt.replace(/[^0-9]/g, "")}`,
63
+ workspaceId,
64
+ detectedAt: checkedAt,
65
+ });
66
+ return ok(outputFormat, result, [
67
+ "AppFleet billing failure detection.",
68
+ `workspace=${workspaceId}`,
69
+ `reason=${result.reason}`,
70
+ `nextAction=${result.nextAction}`,
71
+ "providerApiCallsMade=false",
72
+ ]);
73
+ }
74
+ if (namespace === "cost" && action === "summary") {
75
+ const result = {
76
+ type: "cost_summary_report",
77
+ version: 1,
78
+ workspaceId,
79
+ checkedAt,
80
+ records: [
81
+ createCostUsageRecordMetadata({
82
+ type: "cost_usage_record_metadata",
83
+ version: 1,
84
+ id: `cost_${workspaceId}_${checkedAt.replace(/[^0-9]/g, "")}`,
85
+ workspaceId,
86
+ source: "manual_metadata",
87
+ category: "other",
88
+ quantity: 0,
89
+ unit: "usd_cent",
90
+ amountCents: 0,
91
+ currency: "usd",
92
+ usageStartedAt: checkedAt,
93
+ usageEndedAt: checkedAt,
94
+ recordedAt: checkedAt,
95
+ productionReady: false,
96
+ productionReadiness: "local_test_metadata_persistence",
97
+ providerApiCallsMade: false,
98
+ containsPaymentMethodDetails: false,
99
+ containsProviderPayload: false,
100
+ containsCredentialValues: false,
101
+ storesDecryptedMaterial: false,
102
+ }),
103
+ ],
104
+ totalCents: 0,
105
+ currency: "usd",
106
+ providerApiCallsMade: false,
107
+ containsPaymentMethodDetails: false,
108
+ containsProviderPayload: false,
109
+ };
110
+ return ok(outputFormat, result, [
111
+ "AppFleet cost summary.",
112
+ `workspace=${workspaceId}`,
113
+ "totalUsd=0.00",
114
+ "providerApiCallsMade=false",
115
+ ]);
116
+ }
117
+ if (namespace === "billing" && action === "kms") {
118
+ const result = createCustomerManagedKmsMetadata({
119
+ type: "customer_managed_kms_metadata",
120
+ version: 1,
121
+ id: `kms_${workspaceId}`,
122
+ workspaceId,
123
+ provider: "external_key_manager",
124
+ keyReferenceLabel: "customer-managed-key",
125
+ status: "not_configured",
126
+ createdAt: checkedAt,
127
+ updatedAt: checkedAt,
128
+ productionReady: false,
129
+ productionReadiness: "local_test_metadata_persistence",
130
+ customerManaged: true,
131
+ providerApiCallsMade: false,
132
+ failClosedWithoutProviderCredentials: true,
133
+ containsPlaintextKeyMaterial: false,
134
+ containsCredentialValues: false,
135
+ containsProviderPayload: false,
136
+ storesDecryptedMaterial: false,
137
+ });
138
+ return ok(outputFormat, result, [
139
+ "AppFleet customer-managed KMS.",
140
+ `workspace=${workspaceId}`,
141
+ `status=${result.status}`,
142
+ "providerApiCallsMade=false",
143
+ "containsPlaintextKeyMaterial=false",
144
+ ]);
145
+ }
146
+ }
147
+ catch (error) {
148
+ return {
149
+ exitCode: 1,
150
+ stdout: "",
151
+ stderr: `AppFleet billing/cost failed: ${errorMessage(error)}\n`,
152
+ };
153
+ }
154
+ return {
155
+ exitCode: 1,
156
+ stdout: "",
157
+ stderr: "AppFleet billing/cost failed: usage: billing <plan|status|failures|kms> or cost summary\n",
158
+ };
159
+ }
160
+ function readBillingPlan(argv) {
161
+ const value = readFlag(argv, "--plan") ?? "free";
162
+ if (value === "free" || value === "team" || value === "business") {
163
+ return value;
164
+ }
165
+ throw new Error(`unsupported billing plan "${value}"`);
166
+ }
167
+ function readFlag(argv, flag) {
168
+ const index = argv.indexOf(flag);
169
+ if (index === -1) {
170
+ return undefined;
171
+ }
172
+ const value = argv[index + 1];
173
+ return value && !value.startsWith("--") ? value : undefined;
174
+ }
175
+ function ok(outputFormat, value, humanLines) {
176
+ return {
177
+ exitCode: 0,
178
+ stdout: outputFormat === "json"
179
+ ? `${JSON.stringify(value, null, 2)}\n`
180
+ : `${humanLines.join("\n")}\n`,
181
+ stderr: "",
182
+ };
183
+ }
184
+ function errorMessage(error) {
185
+ return error instanceof Error ? error.message : String(error);
186
+ }
@@ -0,0 +1,124 @@
1
+ import { type AppProjectMemory } from "@appfleet/domain";
2
+ export type CloudSessionCommandResult = {
3
+ exitCode: number;
4
+ stdout: string;
5
+ stderr: string;
6
+ };
7
+ export type CloudSessionCommandOptions = {
8
+ sessionPath?: string;
9
+ projectMemoryStorePath?: string;
10
+ metadataSyncStorePath?: string;
11
+ projectRoot?: string;
12
+ env?: NodeJS.ProcessEnv;
13
+ transport?: ProductionCloudTransport;
14
+ hostedAuthTransport?: HostedAuthTransport;
15
+ now?: () => Date;
16
+ productionSyncMaxRetries?: number;
17
+ hostedAuthTimeoutMs?: number;
18
+ };
19
+ type CloudMode = "local-test" | "production";
20
+ type ProductionCloudConfig = {
21
+ enabled: boolean;
22
+ apiBaseUrl?: string;
23
+ authToken?: string;
24
+ databaseDsn?: string;
25
+ sources: {
26
+ enabled: "flag" | "env" | "default";
27
+ apiBaseUrl: "flag" | "env" | "missing";
28
+ authToken: "env" | "device_store" | "missing";
29
+ databaseDsn: "env" | "missing";
30
+ };
31
+ };
32
+ type RedactedCloudConfigReport = {
33
+ type: "cloud_config_report";
34
+ mode: CloudMode;
35
+ productionCloudEnabled: boolean;
36
+ api: {
37
+ baseUrl: string | undefined;
38
+ source: ProductionCloudConfig["sources"]["apiBaseUrl"];
39
+ auth: {
40
+ bearerToken: "configured_redacted" | "missing";
41
+ source: ProductionCloudConfig["sources"]["authToken"];
42
+ };
43
+ };
44
+ database: {
45
+ dsn: "configured_redacted" | "missing";
46
+ source: ProductionCloudConfig["sources"]["databaseDsn"];
47
+ };
48
+ redaction: {
49
+ tokens: "redacted";
50
+ cookies: "redacted";
51
+ dsns: "redacted";
52
+ secrets: "redacted";
53
+ commandOutput: "omitted";
54
+ encryptedBlobIds: "omitted";
55
+ keyManagementMaterial: "omitted";
56
+ };
57
+ };
58
+ export type ProductionCloudMetadataSyncHttpRequest = {
59
+ method: "POST";
60
+ url: string;
61
+ headers: {
62
+ accept: "application/json";
63
+ contentType: "application/json";
64
+ authorization: "bearer_env_redacted";
65
+ idempotencyKey: string;
66
+ };
67
+ body: {
68
+ idempotencyKey: string;
69
+ workspaceId: string;
70
+ createdAt: string;
71
+ envelopes: Array<{
72
+ type: "project_memory_sync";
73
+ version: 1;
74
+ workspaceId: string;
75
+ projectId: string;
76
+ createdAt: string;
77
+ memory: AppProjectMemory;
78
+ }>;
79
+ };
80
+ redaction: RedactedCloudConfigReport["redaction"];
81
+ };
82
+ export type ProductionCloudTransport = (request: ProductionCloudMetadataSyncHttpRequest, context: {
83
+ authToken: string;
84
+ configReport: RedactedCloudConfigReport;
85
+ }) => Promise<{
86
+ acceptedProjectIds: string[];
87
+ rejectedProjectIds: string[];
88
+ }>;
89
+ type HostedAuthHttpRequest = {
90
+ method: "POST";
91
+ url: string;
92
+ headers: {
93
+ accept: "application/json";
94
+ contentType: "application/json";
95
+ };
96
+ body: {
97
+ workspaceId?: string;
98
+ email?: string;
99
+ sessionId?: string;
100
+ };
101
+ redaction: RedactedCloudConfigReport["redaction"];
102
+ };
103
+ export type HostedAuthTransport = (request: HostedAuthHttpRequest, context: {
104
+ configReport: RedactedCloudConfigReport;
105
+ action: "login" | "logout";
106
+ timeoutMs: number;
107
+ }) => Promise<{
108
+ sessionId?: string;
109
+ workspaceId?: string;
110
+ userId?: string;
111
+ email?: string;
112
+ expiresAt?: string;
113
+ revoked?: boolean;
114
+ authToken?: string;
115
+ }>;
116
+ export declare function runCloudSessionCommand(argv: string[], options?: CloudSessionCommandOptions): Promise<CloudSessionCommandResult>;
117
+ export declare function buildProductionCloudMetadataSyncRequest(input: {
118
+ apiBaseUrl: string;
119
+ workspaceId: string;
120
+ createdAt: string;
121
+ memories: AppProjectMemory[];
122
+ idempotencyKey?: string;
123
+ }): ProductionCloudMetadataSyncHttpRequest;
124
+ export {};