@coreplane/switchboard 1.266.1 → 1.267.1
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/dist/assets/deploy/cloudflare-memory/package.json +1 -1
- package/dist/assets/deploy/cloudflare-memory/tsconfig.json +1 -1
- package/dist/assets/deploy/cloudflare-resident/gc.ts +1 -0
- package/dist/assets/deploy/cloudflare-resident/keyedAsyncLock.ts +20 -0
- package/dist/assets/deploy/cloudflare-resident/legacyCredentials.ts +57 -0
- package/dist/assets/deploy/cloudflare-resident/orphanThreadUsers.ts +14 -0
- package/dist/assets/deploy/cloudflare-resident/worker.ts +646 -204
- package/dist/assets/package-lock.json +4003 -4040
- package/dist/assets/package.json +2 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/core/budgets.ts +10 -0
- package/dist/assets/src/core/coordinator/contract.ts +59 -1
- package/dist/assets/src/core/coordinator/driver.ts +34 -5
- package/dist/assets/src/core/runRecord.ts +38 -3
- package/dist/assets/src/core/ship/coordinator.ts +121 -39
- package/dist/assets/src/core/trace/attrs.ts +3 -0
- package/dist/assets/src/execution/githubMintScope.ts +49 -0
- package/dist/assets/src/execution/residentCredentials.ts +9 -37
- package/dist/assets/src/execution/residentDestroyGate.ts +12 -0
- package/dist/assets/src/execution/residentPoolSpends.ts +96 -0
- package/dist/assets/src/execution/residentRecreateAdmission.ts +41 -0
- package/dist/assets/src/execution/residentSteps.ts +1 -0
- package/dist/assets/web/dist/.vite/manifest.json +67 -67
- package/dist/assets/web/dist/assets/{DeliveryPage-BUJckSuW.js → DeliveryPage-QyS6_Q0m.js} +1 -1
- package/dist/assets/web/dist/assets/{HomePage-BfpIrKDX.js → HomePage-Bm40r0wF.js} +1 -1
- package/dist/assets/web/dist/assets/{PendingTurnRow-C6hroa00.js → PendingTurnRow-BqY-EaZR.js} +1 -1
- package/dist/assets/web/dist/assets/{PlanePage-C2Cb1mkT.js → PlanePage-VmzjNLAz.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentDetailPage-Bd9_Q87l.js → ResidentDetailPage-hjX3tRCh.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentsIndexPage-C8eS7hxp.js → ResidentsIndexPage-BKGnlCEh.js} +1 -1
- package/dist/assets/web/dist/assets/{RunFoldRow-DrfFauYJ.js → RunFoldRow-BOWvSoIv.js} +1 -1
- package/dist/assets/web/dist/assets/{RunRoutePage-B_eXfNAF.js → RunRoutePage-D9ztf-uE.js} +3 -3
- package/dist/assets/web/dist/assets/{RunsIndexPage-BpQu36Dm.js → RunsIndexPage-DTYQbhtr.js} +1 -1
- package/dist/assets/web/dist/assets/{ScheduledPage-DGHcjcSt.js → ScheduledPage-BAYR0tuv.js} +1 -1
- package/dist/assets/web/dist/assets/{SettingsPage-IfQ71QjN.js → SettingsPage-BlYBmCG9.js} +1 -1
- package/dist/assets/web/dist/assets/{SilentTurn-D9ON15a8.js → SilentTurn-CHfzRQW8.js} +1 -1
- package/dist/assets/web/dist/assets/{StatusDot-DyFjGg4V.js → StatusDot-BMkUFVE8.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-BvPkV4kZ.js → Tooltip-Dfot16IA.js} +1 -1
- package/dist/assets/web/dist/assets/{UnitRoutePage-BNe3nBIG.js → UnitRoutePage-BdZoT-4d.js} +1 -1
- package/dist/assets/web/dist/assets/budgets-CJHiTZmM.js +1 -0
- package/dist/assets/web/dist/assets/{dist-v3Py3_QR.js → dist-XRrc0hhm.js} +1 -1
- package/dist/assets/web/dist/assets/{indexRow-BsWcHHVh.js → indexRow-BGjjLnN5.js} +1 -1
- package/dist/assets/web/dist/assets/{main-ks3kBojV.js → main-B32N1vUh.js} +2 -2
- package/dist/assets/web/dist/assets/{sseReplay-DuALaBPq.js → sseReplay-BsNQPLmJ.js} +2 -2
- package/dist/cli.js +1708 -625
- package/package.json +1 -1
- package/dist/assets/web/dist/assets/budgets-BQgcCyvb.js +0 -1
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"secrets": "npm --prefix ../.. run --silent cli -- deploy secrets memory"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@cloudflare/vitest-
|
|
19
|
+
"@cloudflare/vitest-plugin": "1.2.8",
|
|
20
20
|
"@cloudflare/workers-types": "^5.20260914.1",
|
|
21
21
|
"typescript": "^5.9.3",
|
|
22
22
|
"vitest": "^4.1.11",
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"module": "esnext",
|
|
8
8
|
"moduleResolution": "bundler",
|
|
9
9
|
"lib": ["esnext"],
|
|
10
|
-
"types": ["@cloudflare/workers-types/experimental", "@cloudflare/vitest-
|
|
10
|
+
"types": ["@cloudflare/workers-types/experimental", "@cloudflare/vitest-plugin/types", "node"],
|
|
11
11
|
"strict": true,
|
|
12
12
|
"noEmit": true,
|
|
13
13
|
"skipLibCheck": true,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Serialize one key through the entire operation, including rollback. */
|
|
2
|
+
export class KeyedAsyncLock {
|
|
3
|
+
private readonly tails = new Map<string, Promise<void>>();
|
|
4
|
+
|
|
5
|
+
async run<T>(key: string, action: () => Promise<T>): Promise<T> {
|
|
6
|
+
const prior = this.tails.get(key);
|
|
7
|
+
let release!: () => void;
|
|
8
|
+
const current = new Promise<void>((resolve) => {
|
|
9
|
+
release = resolve;
|
|
10
|
+
});
|
|
11
|
+
this.tails.set(key, current);
|
|
12
|
+
if (prior) await prior;
|
|
13
|
+
try {
|
|
14
|
+
return await action();
|
|
15
|
+
} finally {
|
|
16
|
+
release();
|
|
17
|
+
if (this.tails.get(key) === current) this.tails.delete(key);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -41,3 +41,60 @@ remove(sys.argv[2], 'cred')
|
|
|
41
41
|
export function legacyCredentialScrubCommand(worktreePath: string, stageDir: string): string[] {
|
|
42
42
|
return ["python3", "-c", LEGACY_CREDENTIAL_SCRUB, `${worktreePath}/.git`, stageDir];
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
/** A free UID must not inherit files left in its old staging directory.
|
|
46
|
+
* Inspect directory components without following links and report only whether
|
|
47
|
+
* content exists. The probe never reads or prints a credential or staged file. */
|
|
48
|
+
export const LEGACY_STAGE_CONTENT_SCAN = `import os, sys
|
|
49
|
+
|
|
50
|
+
flags = os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW
|
|
51
|
+
fd = os.open('/', flags)
|
|
52
|
+
try:
|
|
53
|
+
try:
|
|
54
|
+
for part in sys.argv[1].strip('/').split('/'):
|
|
55
|
+
if not part:
|
|
56
|
+
continue
|
|
57
|
+
child = os.open(part, flags, dir_fd=fd)
|
|
58
|
+
os.close(fd)
|
|
59
|
+
fd = child
|
|
60
|
+
except FileNotFoundError:
|
|
61
|
+
print('clean')
|
|
62
|
+
else:
|
|
63
|
+
print('stale' if os.listdir(fd) else 'clean')
|
|
64
|
+
finally:
|
|
65
|
+
os.close(fd)
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
export function legacyStageContentScanCommand(stageDir: string): string[] {
|
|
69
|
+
return ["python3", "-c", LEGACY_STAGE_CONTENT_SCAN, stageDir];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Eviction may free this UID only after its known staging files are gone.
|
|
73
|
+
* Unlink through an unfollowed directory descriptor; a linked stage fails. */
|
|
74
|
+
export const LEGACY_STAGE_REUSE_SCRUB = `import os, sys
|
|
75
|
+
|
|
76
|
+
flags = os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW
|
|
77
|
+
fd = os.open('/', flags)
|
|
78
|
+
try:
|
|
79
|
+
try:
|
|
80
|
+
for part in sys.argv[1].strip('/').split('/'):
|
|
81
|
+
if not part:
|
|
82
|
+
continue
|
|
83
|
+
child = os.open(part, flags, dir_fd=fd)
|
|
84
|
+
os.close(fd)
|
|
85
|
+
fd = child
|
|
86
|
+
except FileNotFoundError:
|
|
87
|
+
pass
|
|
88
|
+
else:
|
|
89
|
+
for name in ('cred', 'put'):
|
|
90
|
+
try:
|
|
91
|
+
os.unlink(name, dir_fd=fd)
|
|
92
|
+
except FileNotFoundError:
|
|
93
|
+
pass
|
|
94
|
+
finally:
|
|
95
|
+
os.close(fd)
|
|
96
|
+
`;
|
|
97
|
+
|
|
98
|
+
export function legacyStageReuseScrubCommand(stageDir: string): string[] {
|
|
99
|
+
return ["python3", "-c", LEGACY_STAGE_REUSE_SCRUB, stageDir];
|
|
100
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Validate the root-owned scan before a pool UID can be handed to another run.
|
|
2
|
+
* An older release may have removed a binding without removing its tree. */
|
|
3
|
+
export function hasUnexpectedOwnedThreadDir(output: string, root: string, allowed: readonly string[]): boolean {
|
|
4
|
+
const pathOk = (path: string) => {
|
|
5
|
+
if (!path.startsWith(`${root}/`)) return false;
|
|
6
|
+
const name = path.slice(root.length + 1);
|
|
7
|
+
return name.length > 0 && /^[A-Za-z0-9._-]+$/.test(name) && name !== "." && name !== "..";
|
|
8
|
+
};
|
|
9
|
+
if (allowed.some((path) => !pathOk(path))) return true;
|
|
10
|
+
if (output === "") return false;
|
|
11
|
+
if (!output.endsWith("\n")) return true;
|
|
12
|
+
const paths = output.slice(0, -1).split("\n");
|
|
13
|
+
return paths.some((path) => !pathOk(path) || !allowed.includes(path));
|
|
14
|
+
}
|