@agent-finops/core 0.9.2 → 0.9.3
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
2
|
import { execFile as execFileCallback } from "node:child_process";
|
|
3
|
-
import { open, lstat, mkdir, chmod, realpath, rename, unlink } from "node:fs/promises";
|
|
3
|
+
import { open, lstat, mkdir, chmod, readdir, realpath, rename, unlink } from "node:fs/promises";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { dirname, join, relative, resolve } from "node:path";
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
@@ -343,12 +343,48 @@ async function ensureDefaultParent(homeDirectory, create) {
|
|
|
343
343
|
throw new ActivitySnapshotCacheError("unsafe_directory", "The private aibill directory is not a real directory.");
|
|
344
344
|
}
|
|
345
345
|
if (!create && !hasPrivatePermissions(info.mode)) {
|
|
346
|
-
|
|
346
|
+
// Self-heal (NEW-B1): 0.9.2's signup/telemetry state writers created
|
|
347
|
+
// ~/.aibill without a mode (755 under the default umask), which this
|
|
348
|
+
// guard then refused forever — bricking init on machines that ran
|
|
349
|
+
// 0.9.2 in that window. When the directory holds ONLY our own state
|
|
350
|
+
// files (no cache/ yet, nothing foreign), tighten it to 0700 and
|
|
351
|
+
// proceed instead of erroring. Anything unexpected still refuses.
|
|
352
|
+
if (!await selfHealStateOnlyAibillDirectory(parent)) {
|
|
353
|
+
throw new ActivitySnapshotCacheError("unsafe_directory", `The private aibill directory is not private. Fix: chmod 700 ${parent}`);
|
|
354
|
+
}
|
|
347
355
|
}
|
|
348
356
|
if (create)
|
|
349
357
|
await chmod(parent, 0o700);
|
|
350
358
|
await ensureDefaultCacheGitPrivacy(parent, create);
|
|
351
359
|
}
|
|
360
|
+
/**
|
|
361
|
+
* The complete set of files aibill's own home-scope state writers may have
|
|
362
|
+
* placed in ~/.aibill before any cache exists. A directory with exactly
|
|
363
|
+
* these contents (or fewer) is provably ours to repair.
|
|
364
|
+
*/
|
|
365
|
+
const selfHealableAibillEntries = new Set([
|
|
366
|
+
"signup.json",
|
|
367
|
+
"signup.json.tmp",
|
|
368
|
+
"telemetry.json",
|
|
369
|
+
"telemetry.json.tmp",
|
|
370
|
+
".gitignore"
|
|
371
|
+
]);
|
|
372
|
+
async function selfHealStateOnlyAibillDirectory(parent) {
|
|
373
|
+
try {
|
|
374
|
+
const entries = await readdir(parent, { withFileTypes: true });
|
|
375
|
+
for (const entry of entries) {
|
|
376
|
+
if (!entry.isFile() || !selfHealableAibillEntries.has(entry.name))
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
await chmod(parent, 0o700);
|
|
380
|
+
const confirmed = await lstat(parent);
|
|
381
|
+
return !confirmed.isSymbolicLink() && confirmed.isDirectory() &&
|
|
382
|
+
hasPrivatePermissions(confirmed.mode);
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
352
388
|
/**
|
|
353
389
|
* When a synthetic or real HOME is itself inside a Git worktree, protect the
|
|
354
390
|
* complete top-level private state directory before any cache child is
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One intentionally pragmatic normalizer for waitlist and receipt-email
|
|
3
|
+
* boundaries. It prevents header/control injection without pretending to be
|
|
4
|
+
* a mailbox-verification system; ownership still requires an explicit flow.
|
|
5
|
+
*/
|
|
6
|
+
export declare function normalizeAibillEmailAddress(value: string): string | undefined;
|
|
7
|
+
//# sourceMappingURL=emailAddress.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const controlCharacters = /[\u0000-\u001F\u007F]/u;
|
|
2
|
+
const pragmaticEmailAddress = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u;
|
|
3
|
+
/**
|
|
4
|
+
* One intentionally pragmatic normalizer for waitlist and receipt-email
|
|
5
|
+
* boundaries. It prevents header/control injection without pretending to be
|
|
6
|
+
* a mailbox-verification system; ownership still requires an explicit flow.
|
|
7
|
+
*/
|
|
8
|
+
export function normalizeAibillEmailAddress(value) {
|
|
9
|
+
const normalized = value.trim().toLowerCase();
|
|
10
|
+
if (normalized.length < 3 || normalized.length > 254)
|
|
11
|
+
return undefined;
|
|
12
|
+
if (controlCharacters.test(normalized))
|
|
13
|
+
return undefined;
|
|
14
|
+
return pragmaticEmailAddress.test(normalized) ? normalized : undefined;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=emailAddress.js.map
|
package/dist/receiptShare.d.ts
CHANGED
|
@@ -27,9 +27,9 @@ export declare const receiptShareCardV0Schema: z.ZodObject<{
|
|
|
27
27
|
currency: z.ZodLiteral<"USD">;
|
|
28
28
|
windowDays: z.ZodNumber;
|
|
29
29
|
mode: z.ZodEnum<{
|
|
30
|
-
"local-logs": "local-logs";
|
|
31
|
-
connected: "connected";
|
|
32
30
|
mixed: "mixed";
|
|
31
|
+
connected: "connected";
|
|
32
|
+
"local-logs": "local-logs";
|
|
33
33
|
}>;
|
|
34
34
|
financials: z.ZodObject<{
|
|
35
35
|
subscriptionCommitted: z.ZodObject<{
|
|
@@ -47,8 +47,8 @@ export declare const receiptShareCardV0Schema: z.ZodObject<{
|
|
|
47
47
|
providerBilled: z.ZodObject<{
|
|
48
48
|
amountUsd: z.ZodNullable<z.ZodNumber>;
|
|
49
49
|
financialEvidence: z.ZodEnum<{
|
|
50
|
-
missing: "missing";
|
|
51
50
|
verified: "verified";
|
|
51
|
+
missing: "missing";
|
|
52
52
|
}>;
|
|
53
53
|
}, z.core.$strict>;
|
|
54
54
|
blended: z.ZodNull;
|
|
@@ -58,9 +58,9 @@ export declare const receiptShareCardV0Schema: z.ZodObject<{
|
|
|
58
58
|
recordCount: z.ZodNumber;
|
|
59
59
|
confidence: z.ZodEnum<{
|
|
60
60
|
estimated: "estimated";
|
|
61
|
-
missing: "missing";
|
|
62
61
|
verified: "verified";
|
|
63
62
|
detected_unverified: "detected_unverified";
|
|
63
|
+
missing: "missing";
|
|
64
64
|
}>;
|
|
65
65
|
cuts: z.ZodArray<z.ZodObject<{
|
|
66
66
|
template: z.ZodEnum<{
|
|
@@ -89,9 +89,9 @@ export declare const receiptEmailRequestV0Schema: z.ZodObject<{
|
|
|
89
89
|
currency: z.ZodLiteral<"USD">;
|
|
90
90
|
windowDays: z.ZodNumber;
|
|
91
91
|
mode: z.ZodEnum<{
|
|
92
|
-
"local-logs": "local-logs";
|
|
93
|
-
connected: "connected";
|
|
94
92
|
mixed: "mixed";
|
|
93
|
+
connected: "connected";
|
|
94
|
+
"local-logs": "local-logs";
|
|
95
95
|
}>;
|
|
96
96
|
financials: z.ZodObject<{
|
|
97
97
|
subscriptionCommitted: z.ZodObject<{
|
|
@@ -109,8 +109,8 @@ export declare const receiptEmailRequestV0Schema: z.ZodObject<{
|
|
|
109
109
|
providerBilled: z.ZodObject<{
|
|
110
110
|
amountUsd: z.ZodNullable<z.ZodNumber>;
|
|
111
111
|
financialEvidence: z.ZodEnum<{
|
|
112
|
-
missing: "missing";
|
|
113
112
|
verified: "verified";
|
|
113
|
+
missing: "missing";
|
|
114
114
|
}>;
|
|
115
115
|
}, z.core.$strict>;
|
|
116
116
|
blended: z.ZodNull;
|
|
@@ -120,9 +120,9 @@ export declare const receiptEmailRequestV0Schema: z.ZodObject<{
|
|
|
120
120
|
recordCount: z.ZodNumber;
|
|
121
121
|
confidence: z.ZodEnum<{
|
|
122
122
|
estimated: "estimated";
|
|
123
|
-
missing: "missing";
|
|
124
123
|
verified: "verified";
|
|
125
124
|
detected_unverified: "detected_unverified";
|
|
125
|
+
missing: "missing";
|
|
126
126
|
}>;
|
|
127
127
|
cuts: z.ZodArray<z.ZodObject<{
|
|
128
128
|
template: z.ZodEnum<{
|