@tickernelz/paperclip-pro-adapter-codex-local 2026.925.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 +22 -0
- package/dist/cli/format-event.d.ts +2 -0
- package/dist/cli/format-event.d.ts.map +1 -0
- package/dist/cli/format-event.js +218 -0
- package/dist/cli/format-event.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/quota-probe.d.ts +3 -0
- package/dist/cli/quota-probe.d.ts.map +1 -0
- package/dist/cli/quota-probe.js +104 -0
- package/dist/cli/quota-probe.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +155 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +58 -0
- package/dist/index.test.js.map +1 -0
- package/dist/server/acp.d.ts +30 -0
- package/dist/server/acp.d.ts.map +1 -0
- package/dist/server/acp.js +532 -0
- package/dist/server/acp.js.map +1 -0
- package/dist/server/acp.test.d.ts +2 -0
- package/dist/server/acp.test.d.ts.map +1 -0
- package/dist/server/acp.test.js +1087 -0
- package/dist/server/acp.test.js.map +1 -0
- package/dist/server/adapter-auth-promotion.d.ts +102 -0
- package/dist/server/adapter-auth-promotion.d.ts.map +1 -0
- package/dist/server/adapter-auth-promotion.js +210 -0
- package/dist/server/adapter-auth-promotion.js.map +1 -0
- package/dist/server/adapter-auth-promotion.test.d.ts +2 -0
- package/dist/server/adapter-auth-promotion.test.d.ts.map +1 -0
- package/dist/server/adapter-auth-promotion.test.js +658 -0
- package/dist/server/adapter-auth-promotion.test.js.map +1 -0
- package/dist/server/auth-check.d.ts +8 -0
- package/dist/server/auth-check.d.ts.map +1 -0
- package/dist/server/auth-check.js +8 -0
- package/dist/server/auth-check.js.map +1 -0
- package/dist/server/auth-precedence.d.ts +16 -0
- package/dist/server/auth-precedence.d.ts.map +1 -0
- package/dist/server/auth-precedence.js +20 -0
- package/dist/server/auth-precedence.js.map +1 -0
- package/dist/server/auth-precedence.test.d.ts +2 -0
- package/dist/server/auth-precedence.test.d.ts.map +1 -0
- package/dist/server/auth-precedence.test.js +100 -0
- package/dist/server/auth-precedence.test.js.map +1 -0
- package/dist/server/codex-args.d.ts +13 -0
- package/dist/server/codex-args.d.ts.map +1 -0
- package/dist/server/codex-args.js +76 -0
- package/dist/server/codex-args.js.map +1 -0
- package/dist/server/codex-args.test.d.ts +2 -0
- package/dist/server/codex-args.test.d.ts.map +1 -0
- package/dist/server/codex-args.test.js +243 -0
- package/dist/server/codex-args.test.js.map +1 -0
- package/dist/server/codex-auth-cache.d.ts +206 -0
- package/dist/server/codex-auth-cache.d.ts.map +1 -0
- package/dist/server/codex-auth-cache.js +501 -0
- package/dist/server/codex-auth-cache.js.map +1 -0
- package/dist/server/codex-auth-cache.test.d.ts +2 -0
- package/dist/server/codex-auth-cache.test.d.ts.map +1 -0
- package/dist/server/codex-auth-cache.test.js +497 -0
- package/dist/server/codex-auth-cache.test.js.map +1 -0
- package/dist/server/codex-auth-copyback.d.ts +53 -0
- package/dist/server/codex-auth-copyback.d.ts.map +1 -0
- package/dist/server/codex-auth-copyback.js +123 -0
- package/dist/server/codex-auth-copyback.js.map +1 -0
- package/dist/server/codex-auth-copyback.test.d.ts +2 -0
- package/dist/server/codex-auth-copyback.test.d.ts.map +1 -0
- package/dist/server/codex-auth-copyback.test.js +507 -0
- package/dist/server/codex-auth-copyback.test.js.map +1 -0
- package/dist/server/codex-auth-merge-decision.cjs +170 -0
- package/dist/server/codex-auth-merge-decision.d.ts +22 -0
- package/dist/server/codex-auth-merge-decision.d.ts.map +1 -0
- package/dist/server/codex-auth-merge-decision.js +58 -0
- package/dist/server/codex-auth-merge-decision.js.map +1 -0
- package/dist/server/codex-auth-merge-decision.test.d.ts +2 -0
- package/dist/server/codex-auth-merge-decision.test.d.ts.map +1 -0
- package/dist/server/codex-auth-merge-decision.test.js +230 -0
- package/dist/server/codex-auth-merge-decision.test.js.map +1 -0
- package/dist/server/codex-auth-merge-extract.sh +73 -0
- package/dist/server/codex-auth-merge-scripts.d.ts +21 -0
- package/dist/server/codex-auth-merge-scripts.d.ts.map +1 -0
- package/dist/server/codex-auth-merge-scripts.js +40 -0
- package/dist/server/codex-auth-merge-scripts.js.map +1 -0
- package/dist/server/codex-auth-merge.test.d.ts +2 -0
- package/dist/server/codex-auth-merge.test.d.ts.map +1 -0
- package/dist/server/codex-auth-merge.test.js +714 -0
- package/dist/server/codex-auth-merge.test.js.map +1 -0
- package/dist/server/codex-auth-seed-write.d.ts +30 -0
- package/dist/server/codex-auth-seed-write.d.ts.map +1 -0
- package/dist/server/codex-auth-seed-write.js +41 -0
- package/dist/server/codex-auth-seed-write.js.map +1 -0
- package/dist/server/codex-home.d.ts +162 -0
- package/dist/server/codex-home.d.ts.map +1 -0
- package/dist/server/codex-home.js +771 -0
- package/dist/server/codex-home.js.map +1 -0
- package/dist/server/codex-home.test.d.ts +2 -0
- package/dist/server/codex-home.test.d.ts.map +1 -0
- package/dist/server/codex-home.test.js +1183 -0
- package/dist/server/codex-home.test.js.map +1 -0
- package/dist/server/config-schema.d.ts +3 -0
- package/dist/server/config-schema.d.ts.map +1 -0
- package/dist/server/config-schema.js +67 -0
- package/dist/server/config-schema.js.map +1 -0
- package/dist/server/device-login-export.d.ts +56 -0
- package/dist/server/device-login-export.d.ts.map +1 -0
- package/dist/server/device-login-export.js +230 -0
- package/dist/server/device-login-export.js.map +1 -0
- package/dist/server/device-login-export.test.d.ts +2 -0
- package/dist/server/device-login-export.test.d.ts.map +1 -0
- package/dist/server/device-login-export.test.js +245 -0
- package/dist/server/device-login-export.test.js.map +1 -0
- package/dist/server/device-login-parse.d.ts +20 -0
- package/dist/server/device-login-parse.d.ts.map +1 -0
- package/dist/server/device-login-parse.js +155 -0
- package/dist/server/device-login-parse.js.map +1 -0
- package/dist/server/device-login-parse.test.d.ts +2 -0
- package/dist/server/device-login-parse.test.d.ts.map +1 -0
- package/dist/server/device-login-parse.test.js +269 -0
- package/dist/server/device-login-parse.test.js.map +1 -0
- package/dist/server/device-login-runner.d.ts +69 -0
- package/dist/server/device-login-runner.d.ts.map +1 -0
- package/dist/server/device-login-runner.js +109 -0
- package/dist/server/device-login-runner.js.map +1 -0
- package/dist/server/device-login-runner.test.d.ts +2 -0
- package/dist/server/device-login-runner.test.d.ts.map +1 -0
- package/dist/server/device-login-runner.test.js +201 -0
- package/dist/server/device-login-runner.test.js.map +1 -0
- package/dist/server/engine-availability.test.d.ts +2 -0
- package/dist/server/engine-availability.test.d.ts.map +1 -0
- package/dist/server/engine-availability.test.js +41 -0
- package/dist/server/engine-availability.test.js.map +1 -0
- package/dist/server/execute.acp-fallback.test.d.ts +2 -0
- package/dist/server/execute.acp-fallback.test.d.ts.map +1 -0
- package/dist/server/execute.acp-fallback.test.js +123 -0
- package/dist/server/execute.acp-fallback.test.js.map +1 -0
- package/dist/server/execute.auth-precedence.test.d.ts +2 -0
- package/dist/server/execute.auth-precedence.test.d.ts.map +1 -0
- package/dist/server/execute.auth-precedence.test.js +137 -0
- package/dist/server/execute.auth-precedence.test.js.map +1 -0
- package/dist/server/execute.auth.test.d.ts +2 -0
- package/dist/server/execute.auth.test.d.ts.map +1 -0
- package/dist/server/execute.auth.test.js +197 -0
- package/dist/server/execute.auth.test.js.map +1 -0
- package/dist/server/execute.d.ts +40 -0
- package/dist/server/execute.d.ts.map +1 -0
- package/dist/server/execute.js +1290 -0
- package/dist/server/execute.js.map +1 -0
- package/dist/server/execute.remote.test.d.ts +2 -0
- package/dist/server/execute.remote.test.d.ts.map +1 -0
- package/dist/server/execute.remote.test.js +556 -0
- package/dist/server/execute.remote.test.js.map +1 -0
- package/dist/server/execute.stderr-error.test.d.ts +2 -0
- package/dist/server/execute.stderr-error.test.d.ts.map +1 -0
- package/dist/server/execute.stderr-error.test.js +142 -0
- package/dist/server/execute.stderr-error.test.js.map +1 -0
- package/dist/server/execute.test.d.ts +2 -0
- package/dist/server/execute.test.d.ts.map +1 -0
- package/dist/server/execute.test.js +240 -0
- package/dist/server/execute.test.js.map +1 -0
- package/dist/server/index.d.ts +18 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +71 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/output-inactivity-monitor.d.ts +62 -0
- package/dist/server/output-inactivity-monitor.d.ts.map +1 -0
- package/dist/server/output-inactivity-monitor.integration.test.d.ts +2 -0
- package/dist/server/output-inactivity-monitor.integration.test.d.ts.map +1 -0
- package/dist/server/output-inactivity-monitor.integration.test.js +136 -0
- package/dist/server/output-inactivity-monitor.integration.test.js.map +1 -0
- package/dist/server/output-inactivity-monitor.js +112 -0
- package/dist/server/output-inactivity-monitor.js.map +1 -0
- package/dist/server/output-inactivity-monitor.test.d.ts +2 -0
- package/dist/server/output-inactivity-monitor.test.d.ts.map +1 -0
- package/dist/server/output-inactivity-monitor.test.js +284 -0
- package/dist/server/output-inactivity-monitor.test.js.map +1 -0
- package/dist/server/parse.d.ts +51 -0
- package/dist/server/parse.d.ts.map +1 -0
- package/dist/server/parse.js +261 -0
- package/dist/server/parse.js.map +1 -0
- package/dist/server/parse.test.d.ts +2 -0
- package/dist/server/parse.test.d.ts.map +1 -0
- package/dist/server/parse.test.js +186 -0
- package/dist/server/parse.test.js.map +1 -0
- package/dist/server/process-activity-monitor.d.ts +21 -0
- package/dist/server/process-activity-monitor.d.ts.map +1 -0
- package/dist/server/process-activity-monitor.js +103 -0
- package/dist/server/process-activity-monitor.js.map +1 -0
- package/dist/server/process-activity-monitor.test.d.ts +2 -0
- package/dist/server/process-activity-monitor.test.d.ts.map +1 -0
- package/dist/server/process-activity-monitor.test.js +82 -0
- package/dist/server/process-activity-monitor.test.js.map +1 -0
- package/dist/server/quota-spawn-error.test.d.ts +2 -0
- package/dist/server/quota-spawn-error.test.d.ts.map +1 -0
- package/dist/server/quota-spawn-error.test.js +174 -0
- package/dist/server/quota-spawn-error.test.js.map +1 -0
- package/dist/server/quota.d.ts +66 -0
- package/dist/server/quota.d.ts.map +1 -0
- package/dist/server/quota.js +511 -0
- package/dist/server/quota.js.map +1 -0
- package/dist/server/runtime-config.d.ts +11 -0
- package/dist/server/runtime-config.d.ts.map +1 -0
- package/dist/server/runtime-config.js +380 -0
- package/dist/server/runtime-config.js.map +1 -0
- package/dist/server/runtime-config.test.d.ts +2 -0
- package/dist/server/runtime-config.test.d.ts.map +1 -0
- package/dist/server/runtime-config.test.js +367 -0
- package/dist/server/runtime-config.test.js.map +1 -0
- package/dist/server/skills.d.ts +8 -0
- package/dist/server/skills.d.ts.map +1 -0
- package/dist/server/skills.js +26 -0
- package/dist/server/skills.js.map +1 -0
- package/dist/server/test.d.ts +3 -0
- package/dist/server/test.d.ts.map +1 -0
- package/dist/server/test.js +458 -0
- package/dist/server/test.js.map +1 -0
- package/dist/server/test.remote.test.d.ts +2 -0
- package/dist/server/test.remote.test.d.ts.map +1 -0
- package/dist/server/test.remote.test.js +396 -0
- package/dist/server/test.remote.test.js.map +1 -0
- package/dist/ui/build-config.d.ts +5 -0
- package/dist/ui/build-config.d.ts.map +1 -0
- package/dist/ui/build-config.js +229 -0
- package/dist/ui/build-config.js.map +1 -0
- package/dist/ui/build-config.test.d.ts +2 -0
- package/dist/ui/build-config.test.d.ts.map +1 -0
- package/dist/ui/build-config.test.js +272 -0
- package/dist/ui/build-config.test.js.map +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +8 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/parse-stdout.d.ts +3 -0
- package/dist/ui/parse-stdout.d.ts.map +1 -0
- package/dist/ui/parse-stdout.js +265 -0
- package/dist/ui/parse-stdout.js.map +1 -0
- package/dist/ui/parse-stdout.test.d.ts +2 -0
- package/dist/ui/parse-stdout.test.d.ts.map +1 -0
- package/dist/ui/parse-stdout.test.js +77 -0
- package/dist/ui/parse-stdout.test.js.map +1 -0
- package/package.json +61 -0
- package/skills/agentmail/SKILL.md +70 -0
- package/skills/paperclip/SKILL.md +712 -0
- package/skills/paperclip/references/api-reference.md +1675 -0
- package/skills/paperclip/references/artifacts.md +158 -0
- package/skills/paperclip/references/cases.md +295 -0
- package/skills/paperclip/references/company-skills.md +266 -0
- package/skills/paperclip/references/issue-workspaces.md +80 -0
- package/skills/paperclip/references/routines.md +231 -0
- package/skills/paperclip/references/workflows.md +141 -0
- package/skills/paperclip/scripts/paperclip-upload-artifact.sh +592 -0
- package/skills/paperclip-board/SKILL.md +619 -0
- package/skills/paperclip-converting-plans-to-tasks/SKILL.md +60 -0
- package/skills/paperclip-create-agent/SKILL.md +179 -0
- package/skills/paperclip-create-agent/references/agent-instruction-templates.md +123 -0
- package/skills/paperclip-create-agent/references/agents/coder.md +64 -0
- package/skills/paperclip-create-agent/references/agents/qa.md +88 -0
- package/skills/paperclip-create-agent/references/agents/securityengineer.md +135 -0
- package/skills/paperclip-create-agent/references/agents/uxdesigner.md +115 -0
- package/skills/paperclip-create-agent/references/api-reference.md +110 -0
- package/skills/paperclip-create-agent/references/baseline-role-guide.md +168 -0
- package/skills/paperclip-create-agent/references/draft-review-checklist.md +95 -0
- package/skills/para-memory-files/SKILL.md +100 -0
- package/skills/para-memory-files/references/schemas.md +35 -0
- package/skills/slack/SKILL.md +65 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { chmod, lstat, mkdir, mkdtemp, readFile, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { MAX_AUTH_JSON_BYTES, deriveProofHome, installDeviceLoginCredential, removeProofHome, resolveProofHomeRoot, } from "./device-login-export.js";
|
|
6
|
+
import { resolveManagedCodexHomeDir, resolveSharedCodexHomeDir } from "./codex-home.js";
|
|
7
|
+
const COMPANY = "company-a";
|
|
8
|
+
const RUN = "run-1234";
|
|
9
|
+
const NEWER = "2026-07-09T02:00:00Z";
|
|
10
|
+
const OLDER = "2026-07-09T01:00:00Z";
|
|
11
|
+
const ACCOUNT = "acct-42";
|
|
12
|
+
const OTHER_ACCOUNT = "acct-99";
|
|
13
|
+
const TOKEN_SENTINEL = "SENTINEL_TOKEN_XYZ";
|
|
14
|
+
describe("device-login credential export", () => {
|
|
15
|
+
const cleanupDirs = [];
|
|
16
|
+
afterEach(async () => {
|
|
17
|
+
while (cleanupDirs.length > 0) {
|
|
18
|
+
const dir = cleanupDirs.pop();
|
|
19
|
+
if (!dir)
|
|
20
|
+
continue;
|
|
21
|
+
await chmod(dir, 0o700).catch(() => undefined);
|
|
22
|
+
await rm(dir, { recursive: true, force: true }).catch(() => undefined);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
async function makeInstanceRoot() {
|
|
26
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "paperclip-codex-proof-"));
|
|
27
|
+
cleanupDirs.push(dir);
|
|
28
|
+
return dir;
|
|
29
|
+
}
|
|
30
|
+
function envFor(instanceHome, extra = {}) {
|
|
31
|
+
return { PAPERCLIP_HOME: instanceHome, PAPERCLIP_INSTANCE_ID: "default", ...extra };
|
|
32
|
+
}
|
|
33
|
+
function subscriptionAuth(input) {
|
|
34
|
+
const suffix = input.marker ?? input.accountId;
|
|
35
|
+
return Buffer.from(JSON.stringify({
|
|
36
|
+
tokens: {
|
|
37
|
+
id_token: `id-token-${suffix}`,
|
|
38
|
+
access_token: `access-token-${suffix}`,
|
|
39
|
+
refresh_token: `${TOKEN_SENTINEL}-${suffix}`,
|
|
40
|
+
account_id: input.accountId,
|
|
41
|
+
},
|
|
42
|
+
...(input.lastRefresh ? { last_refresh: input.lastRefresh } : {}),
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
const noopLog = (_line) => { };
|
|
46
|
+
it("deriveProofHome returns a unique, run-scoped path under a company-scoped root", async () => {
|
|
47
|
+
const home = await makeInstanceRoot();
|
|
48
|
+
const env = envFor(home);
|
|
49
|
+
const root = resolveProofHomeRoot(env, COMPANY);
|
|
50
|
+
expect(root).toBe(path.resolve(home, "instances", "default", "companies", COMPANY, "codex-device-login-proof"));
|
|
51
|
+
const a = deriveProofHome({ env, companyId: COMPANY, runId: RUN });
|
|
52
|
+
const b = deriveProofHome({ env, companyId: COMPANY, runId: RUN });
|
|
53
|
+
expect(a.startsWith(root + path.sep)).toBe(true);
|
|
54
|
+
expect(a).toContain(RUN);
|
|
55
|
+
expect(a).not.toBe(b); // unique per call
|
|
56
|
+
// Never the shared or the managed home.
|
|
57
|
+
expect(a).not.toBe(resolveSharedCodexHomeDir(env));
|
|
58
|
+
expect(a).not.toBe(resolveManagedCodexHomeDir(env, COMPANY));
|
|
59
|
+
});
|
|
60
|
+
it("export_creates_root_and_home_at_mode_0700", async () => {
|
|
61
|
+
const home = await makeInstanceRoot();
|
|
62
|
+
const env = envFor(home);
|
|
63
|
+
const proofHome = deriveProofHome({ env, companyId: COMPANY, runId: RUN });
|
|
64
|
+
await installDeviceLoginCredential({
|
|
65
|
+
sandboxAuthBytes: subscriptionAuth({ accountId: ACCOUNT, lastRefresh: NEWER }),
|
|
66
|
+
proofHome,
|
|
67
|
+
env,
|
|
68
|
+
companyId: COMPANY,
|
|
69
|
+
log: noopLog,
|
|
70
|
+
});
|
|
71
|
+
const root = resolveProofHomeRoot(env, COMPANY);
|
|
72
|
+
expect((await stat(root)).mode & 0o777).toBe(0o700);
|
|
73
|
+
expect((await stat(proofHome)).mode & 0o777).toBe(0o700);
|
|
74
|
+
});
|
|
75
|
+
it("export_seeds_empty_proof_home_with_mode_0600", async () => {
|
|
76
|
+
const home = await makeInstanceRoot();
|
|
77
|
+
const env = envFor(home);
|
|
78
|
+
const proofHome = deriveProofHome({ env, companyId: COMPANY, runId: RUN });
|
|
79
|
+
const outcome = await installDeviceLoginCredential({
|
|
80
|
+
sandboxAuthBytes: subscriptionAuth({ accountId: ACCOUNT, lastRefresh: NEWER }),
|
|
81
|
+
proofHome,
|
|
82
|
+
env,
|
|
83
|
+
companyId: COMPANY,
|
|
84
|
+
log: noopLog,
|
|
85
|
+
});
|
|
86
|
+
expect(outcome).toBe("seeded");
|
|
87
|
+
const authPath = path.join(proofHome, "auth.json");
|
|
88
|
+
expect((await stat(authPath)).mode & 0o777).toBe(0o600);
|
|
89
|
+
const written = JSON.parse(await readFile(authPath, "utf8"));
|
|
90
|
+
expect(written.tokens.account_id).toBe(ACCOUNT);
|
|
91
|
+
});
|
|
92
|
+
it("export_rejects_default_shared_and_managed_home", async () => {
|
|
93
|
+
const home = await makeInstanceRoot();
|
|
94
|
+
const env = envFor(home, { CODEX_HOME: path.join(home, "shared-codex") });
|
|
95
|
+
const bytes = subscriptionAuth({ accountId: ACCOUNT, lastRefresh: NEWER });
|
|
96
|
+
const shared = resolveSharedCodexHomeDir(env);
|
|
97
|
+
const managed = resolveManagedCodexHomeDir(env, COMPANY);
|
|
98
|
+
const agentManaged = path.resolve(managed, "..", "agents", "agent-1", "codex-home");
|
|
99
|
+
for (const target of [shared, managed, agentManaged]) {
|
|
100
|
+
await expect(installDeviceLoginCredential({ sandboxAuthBytes: bytes, proofHome: target, env, companyId: COMPANY, log: noopLog })).rejects.toThrow();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
it("export_rejects_symlink_or_non_regular_path", async () => {
|
|
104
|
+
const home = await makeInstanceRoot();
|
|
105
|
+
const env = envFor(home);
|
|
106
|
+
const root = resolveProofHomeRoot(env, COMPANY);
|
|
107
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
108
|
+
const bytes = subscriptionAuth({ accountId: ACCOUNT, lastRefresh: NEWER });
|
|
109
|
+
// A symlinked proof home is rejected.
|
|
110
|
+
const realDir = path.join(home, "elsewhere");
|
|
111
|
+
await mkdir(realDir, { recursive: true, mode: 0o700 });
|
|
112
|
+
const linkedHome = path.join(root, `${RUN}-linked`);
|
|
113
|
+
await symlink(realDir, linkedHome);
|
|
114
|
+
await expect(installDeviceLoginCredential({ sandboxAuthBytes: bytes, proofHome: linkedHome, env, companyId: COMPANY, log: noopLog })).rejects.toThrow();
|
|
115
|
+
// A proof home whose auth.json is a symlink is rejected.
|
|
116
|
+
const symAuthHome = path.join(root, `${RUN}-symauth`);
|
|
117
|
+
await mkdir(symAuthHome, { recursive: true, mode: 0o700 });
|
|
118
|
+
await symlink(path.join(home, "target.json"), path.join(symAuthHome, "auth.json"));
|
|
119
|
+
await expect(installDeviceLoginCredential({ sandboxAuthBytes: bytes, proofHome: symAuthHome, env, companyId: COMPANY, log: noopLog })).rejects.toThrow();
|
|
120
|
+
});
|
|
121
|
+
it("export_rejects_api_key_malformed_or_oversized_payload", async () => {
|
|
122
|
+
const home = await makeInstanceRoot();
|
|
123
|
+
const env = envFor(home);
|
|
124
|
+
const apiKey = Buffer.from(JSON.stringify({ OPENAI_API_KEY: "sk-secret-key" }));
|
|
125
|
+
const malformed = Buffer.from("this is not json {");
|
|
126
|
+
const oversized = Buffer.concat([
|
|
127
|
+
subscriptionAuth({ accountId: ACCOUNT, lastRefresh: NEWER }),
|
|
128
|
+
Buffer.alloc(MAX_AUTH_JSON_BYTES + 10, 0x20),
|
|
129
|
+
]);
|
|
130
|
+
for (const bytes of [apiKey, malformed, oversized]) {
|
|
131
|
+
const proofHome = deriveProofHome({ env, companyId: COMPANY, runId: RUN });
|
|
132
|
+
await expect(installDeviceLoginCredential({ sandboxAuthBytes: bytes, proofHome, env, companyId: COMPANY, log: noopLog })).rejects.toThrow();
|
|
133
|
+
// No file was written on a rejected payload.
|
|
134
|
+
await expect(stat(path.join(proofHome, "auth.json"))).rejects.toThrow();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
it("export_updates_home_with_strictly_newer_same_identity", async () => {
|
|
138
|
+
const home = await makeInstanceRoot();
|
|
139
|
+
const env = envFor(home);
|
|
140
|
+
const proofHome = deriveProofHome({ env, companyId: COMPANY, runId: RUN });
|
|
141
|
+
const authPath = path.join(proofHome, "auth.json");
|
|
142
|
+
await installDeviceLoginCredential({
|
|
143
|
+
sandboxAuthBytes: subscriptionAuth({ accountId: ACCOUNT, lastRefresh: OLDER, marker: "old" }),
|
|
144
|
+
proofHome,
|
|
145
|
+
env,
|
|
146
|
+
companyId: COMPANY,
|
|
147
|
+
log: noopLog,
|
|
148
|
+
});
|
|
149
|
+
const outcome = await installDeviceLoginCredential({
|
|
150
|
+
sandboxAuthBytes: subscriptionAuth({ accountId: ACCOUNT, lastRefresh: NEWER, marker: "new" }),
|
|
151
|
+
proofHome,
|
|
152
|
+
env,
|
|
153
|
+
companyId: COMPANY,
|
|
154
|
+
log: noopLog,
|
|
155
|
+
});
|
|
156
|
+
expect(outcome).toBe("updated");
|
|
157
|
+
const written = JSON.parse(await readFile(authPath, "utf8"));
|
|
158
|
+
expect(written.last_refresh).toBe(NEWER);
|
|
159
|
+
expect(written.tokens.refresh_token).toContain("new");
|
|
160
|
+
});
|
|
161
|
+
it("export_keeps_home_on_older_or_different_identity", async () => {
|
|
162
|
+
const home = await makeInstanceRoot();
|
|
163
|
+
const env = envFor(home);
|
|
164
|
+
const proofHome = deriveProofHome({ env, companyId: COMPANY, runId: RUN });
|
|
165
|
+
const authPath = path.join(proofHome, "auth.json");
|
|
166
|
+
await installDeviceLoginCredential({
|
|
167
|
+
sandboxAuthBytes: subscriptionAuth({ accountId: ACCOUNT, lastRefresh: NEWER, marker: "keep" }),
|
|
168
|
+
proofHome,
|
|
169
|
+
env,
|
|
170
|
+
companyId: COMPANY,
|
|
171
|
+
log: noopLog,
|
|
172
|
+
});
|
|
173
|
+
// Older same identity: kept.
|
|
174
|
+
const olderOutcome = await installDeviceLoginCredential({
|
|
175
|
+
sandboxAuthBytes: subscriptionAuth({ accountId: ACCOUNT, lastRefresh: OLDER, marker: "older" }),
|
|
176
|
+
proofHome,
|
|
177
|
+
env,
|
|
178
|
+
companyId: COMPANY,
|
|
179
|
+
log: noopLog,
|
|
180
|
+
});
|
|
181
|
+
expect(olderOutcome).toBe("kept");
|
|
182
|
+
// Different identity, even newer: kept.
|
|
183
|
+
const otherOutcome = await installDeviceLoginCredential({
|
|
184
|
+
sandboxAuthBytes: subscriptionAuth({ accountId: OTHER_ACCOUNT, lastRefresh: NEWER, marker: "other" }),
|
|
185
|
+
proofHome,
|
|
186
|
+
env,
|
|
187
|
+
companyId: COMPANY,
|
|
188
|
+
log: noopLog,
|
|
189
|
+
});
|
|
190
|
+
expect(otherOutcome).toBe("kept");
|
|
191
|
+
const written = JSON.parse(await readFile(authPath, "utf8"));
|
|
192
|
+
expect(written.tokens.account_id).toBe(ACCOUNT);
|
|
193
|
+
expect(written.tokens.refresh_token).toContain("keep");
|
|
194
|
+
});
|
|
195
|
+
it("export_logs_contain_no_token_bytes", async () => {
|
|
196
|
+
const home = await makeInstanceRoot();
|
|
197
|
+
const env = envFor(home);
|
|
198
|
+
const proofHome = deriveProofHome({ env, companyId: COMPANY, runId: RUN });
|
|
199
|
+
const logs = [];
|
|
200
|
+
await installDeviceLoginCredential({
|
|
201
|
+
sandboxAuthBytes: subscriptionAuth({ accountId: ACCOUNT, lastRefresh: NEWER }),
|
|
202
|
+
proofHome,
|
|
203
|
+
env,
|
|
204
|
+
companyId: COMPANY,
|
|
205
|
+
log: (line) => {
|
|
206
|
+
logs.push(line);
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
const haystack = logs.join("\n");
|
|
210
|
+
expect(haystack).not.toContain(TOKEN_SENTINEL);
|
|
211
|
+
expect(haystack).not.toContain(ACCOUNT);
|
|
212
|
+
});
|
|
213
|
+
it("cleanup_removes_proof_home_and_leaves_default_home_unchanged", async () => {
|
|
214
|
+
const home = await makeInstanceRoot();
|
|
215
|
+
const env = envFor(home, { CODEX_HOME: path.join(home, "shared-codex") });
|
|
216
|
+
// A sentinel in a fake shared/default home must survive the cleanup.
|
|
217
|
+
const shared = resolveSharedCodexHomeDir(env);
|
|
218
|
+
await mkdir(shared, { recursive: true, mode: 0o700 });
|
|
219
|
+
const sentinel = path.join(shared, "auth.json");
|
|
220
|
+
await writeFile(sentinel, JSON.stringify({ tokens: { account_id: "host", refresh_token: "host" } }), { mode: 0o600 });
|
|
221
|
+
const proofHome = deriveProofHome({ env, companyId: COMPANY, runId: RUN });
|
|
222
|
+
await installDeviceLoginCredential({
|
|
223
|
+
sandboxAuthBytes: subscriptionAuth({ accountId: ACCOUNT, lastRefresh: NEWER }),
|
|
224
|
+
proofHome,
|
|
225
|
+
env,
|
|
226
|
+
companyId: COMPANY,
|
|
227
|
+
log: noopLog,
|
|
228
|
+
});
|
|
229
|
+
expect((await lstat(proofHome)).isDirectory()).toBe(true);
|
|
230
|
+
await removeProofHome(proofHome, { env, companyId: COMPANY });
|
|
231
|
+
await expect(lstat(proofHome)).rejects.toThrow();
|
|
232
|
+
// The fake default home is untouched.
|
|
233
|
+
expect(await readFile(sentinel, "utf8")).toContain("host");
|
|
234
|
+
});
|
|
235
|
+
it("removeProofHome refuses a path outside the proof root", async () => {
|
|
236
|
+
const home = await makeInstanceRoot();
|
|
237
|
+
const env = envFor(home);
|
|
238
|
+
const outside = path.join(home, "not-a-proof-home");
|
|
239
|
+
await mkdir(outside, { recursive: true });
|
|
240
|
+
await expect(removeProofHome(outside, { env, companyId: COMPANY })).rejects.toThrow();
|
|
241
|
+
// The outside directory is untouched.
|
|
242
|
+
expect((await lstat(outside)).isDirectory()).toBe(true);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
//# sourceMappingURL=device-login-export.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-login-export.test.js","sourceRoot":"","sources":["../../src/server/device-login-export.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface DeviceLoginPrompt {
|
|
2
|
+
url: string;
|
|
3
|
+
code: string;
|
|
4
|
+
}
|
|
5
|
+
export declare const DEVICE_LOGIN_URL = "https://auth.openai.com/codex/device";
|
|
6
|
+
/**
|
|
7
|
+
* Parses Codex device-login output. Removes ANSI color sequences first, so
|
|
8
|
+
* colored output from Codex CLI 0.128.0 and later reads the same as plain
|
|
9
|
+
* output. The parser reads the URL first, then finds the "one-time code"
|
|
10
|
+
* preamble after the URL, then reads the code from the dedicated code line right
|
|
11
|
+
* after the preamble line. So the code binds to the prompt line that introduces
|
|
12
|
+
* it, and a code-shaped token between the URL and the preamble, on the preamble
|
|
13
|
+
* line, or mixed with other text on a later line, cannot form a prompt. Returns
|
|
14
|
+
* the login URL and the one-time code when both are present and valid. Returns
|
|
15
|
+
* null for any other input, including a non-string input, an absent prompt, a URL
|
|
16
|
+
* with a query or a fragment, a wrong origin or path, and a malformed short code.
|
|
17
|
+
* Never throws on input, and never puts the URL or the code into a log or an error.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseDeviceLoginPrompt(text: string): DeviceLoginPrompt | null;
|
|
20
|
+
//# sourceMappingURL=device-login-parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-login-parse.d.ts","sourceRoot":"","sources":["../../src/server/device-login-parse.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAID,eAAO,MAAM,gBAAgB,yCAAyC,CAAC;AAgIvE;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAQ7E"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// The device-login output parser. It reads the Codex `login --device-auth`
|
|
2
|
+
// output and returns the login URL and the one-time code, or null.
|
|
3
|
+
//
|
|
4
|
+
// Security (Control 1 — strict validation): the parser accepts only the exact
|
|
5
|
+
// origin and path of the device-login URL. It rejects any query, any fragment, a
|
|
6
|
+
// different origin, and a different path. It accepts only the short-code
|
|
7
|
+
// structure `XXXX-XXXXX` (four characters, a hyphen, then five characters). The
|
|
8
|
+
// parser never logs the URL, the code, or any input byte, and it keeps them out
|
|
9
|
+
// of every thrown error. The parser is a pure function.
|
|
10
|
+
//
|
|
11
|
+
// The parser returns the printed URL after the platform `URL` parser validates
|
|
12
|
+
// and normalizes it. It never returns the raw matched token: the accepting
|
|
13
|
+
// rules above already bound the token to the exact origin, path, and empty
|
|
14
|
+
// query and fragment, so the normalized form carries no unvalidated byte.
|
|
15
|
+
// The one and only accepted device-login origin and path. `findExactDeviceUrl`
|
|
16
|
+
// checks a candidate token against these exact values before it accepts it.
|
|
17
|
+
export const DEVICE_LOGIN_URL = "https://auth.openai.com/codex/device";
|
|
18
|
+
// Codex CLI 0.128.0 and later wrap the login URL and the one-time code in ANSI
|
|
19
|
+
// color sequences. A color sequence is a Control Sequence Introducer (CSI): the
|
|
20
|
+
// ESC control byte, a `[`, zero or more parameter bytes (0x30-0x3F), zero or
|
|
21
|
+
// more intermediate bytes (0x20-0x2F), and one final byte (0x40-0x7E). The
|
|
22
|
+
// parser removes every CSI sequence first, so a colored URL or code reads the
|
|
23
|
+
// same as a plain one. The strip only normalizes the input; the URL and the
|
|
24
|
+
// code still pass the strict validation below.
|
|
25
|
+
// eslint-disable-next-line no-control-regex
|
|
26
|
+
const ANSI_CSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
|
27
|
+
// A candidate URL token is a run of non-space characters that starts with an
|
|
28
|
+
// http or https scheme. The parser validates each candidate with the `URL`
|
|
29
|
+
// class; the regular expression only splits tokens out of the text.
|
|
30
|
+
const URL_TOKEN_RE = /https?:\/\/\S+/g;
|
|
31
|
+
// Trailing punctuation that prose commonly puts right after a URL. The parser
|
|
32
|
+
// strips only these characters. It never strips `?` or `#`, so a URL with a
|
|
33
|
+
// query or a fragment stays malformed and the parser rejects it.
|
|
34
|
+
const TRAILING_PUNCTUATION_RE = /[)\].,;:!]+$/;
|
|
35
|
+
// The one-time code structure on a dedicated line: four characters, a hyphen,
|
|
36
|
+
// then five characters, and nothing else on the line. The Codex prompt prints
|
|
37
|
+
// the code alone on the line that comes right after the preamble line. So the
|
|
38
|
+
// pattern matches the whole line. A code-shaped token that shares a line with
|
|
39
|
+
// other text cannot match. The real code alphabet is a Codex detail that the
|
|
40
|
+
// grounded capture did not keep (the capture redacted the code to `?`). So the
|
|
41
|
+
// parser binds the token class to alphanumerics and the redaction sentinel `?`.
|
|
42
|
+
const CODE_LINE_RE = /^([A-Za-z0-9?]{4}-[A-Za-z0-9?]{5})$/;
|
|
43
|
+
// The prompt line that introduces the one-time code. The Codex prompt prints
|
|
44
|
+
// the phrase "one-time code" on the line right before the code. The parser
|
|
45
|
+
// anchors the code search on this phrase. So the code binds to the prompt line
|
|
46
|
+
// that introduces it. A code-shaped token that sits between the URL and this
|
|
47
|
+
// phrase cannot bind, because the parser reads the code only after the phrase.
|
|
48
|
+
const CODE_PREAMBLE_RE = /one-time code/i;
|
|
49
|
+
// The maximum number of characters between the end of the URL and the start of
|
|
50
|
+
// the code preamble. The Codex prompt prints the preamble about one line after
|
|
51
|
+
// the URL. The parser looks for the preamble only inside this window after the
|
|
52
|
+
// URL. So a preamble far away in the output cannot bind to the URL.
|
|
53
|
+
const MAX_URL_TO_PREAMBLE_GAP = 256;
|
|
54
|
+
// The maximum number of characters after the end of the preamble line that the
|
|
55
|
+
// parser reads for the code. The Codex prompt prints the code on the line right
|
|
56
|
+
// after the preamble line. The parser looks for the code only inside this window
|
|
57
|
+
// after the preamble line. The window is large enough for the real code line and
|
|
58
|
+
// small enough to reject distant noise.
|
|
59
|
+
const MAX_PREAMBLE_TO_CODE_GAP = 128;
|
|
60
|
+
/**
|
|
61
|
+
* Returns the validated device-login URL and its end index when `text` holds it
|
|
62
|
+
* as a standalone token with the exact origin `https://auth.openai.com` and the
|
|
63
|
+
* exact path `/codex/device` and no query, fragment, or credentials. Returns
|
|
64
|
+
* null otherwise. Returns the platform `URL` parser's normalized form of the
|
|
65
|
+
* matched token, so the accepting rules bound the output even though it is not a
|
|
66
|
+
* fixed constant. The `end` index is the position of the first character after
|
|
67
|
+
* the matched token in `text`.
|
|
68
|
+
*/
|
|
69
|
+
function findExactDeviceUrl(text) {
|
|
70
|
+
for (const match of text.matchAll(URL_TOKEN_RE)) {
|
|
71
|
+
const token = match[0];
|
|
72
|
+
const cleaned = token.replace(TRAILING_PUNCTUATION_RE, "");
|
|
73
|
+
let parsed;
|
|
74
|
+
try {
|
|
75
|
+
parsed = new URL(cleaned);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (parsed.protocol === "https:" &&
|
|
81
|
+
parsed.host === "auth.openai.com" &&
|
|
82
|
+
parsed.pathname === "/codex/device" &&
|
|
83
|
+
parsed.search === "" &&
|
|
84
|
+
parsed.hash === "" &&
|
|
85
|
+
parsed.username === "" &&
|
|
86
|
+
parsed.password === "") {
|
|
87
|
+
return { url: parsed.toString(), end: (match.index ?? 0) + token.length };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Returns the one-time code when `text` holds the code preamble after
|
|
94
|
+
* `fromIndex` and a dedicated code line after that preamble line. The parser
|
|
95
|
+
* first finds the "one-time code" preamble in a window after the URL. It then
|
|
96
|
+
* advances past the end of the preamble line and reads the first non-blank line
|
|
97
|
+
* after it. That line must hold only the `XXXX-XXXXX` code. So the code binds to
|
|
98
|
+
* the dedicated code line that the prompt prints right after the preamble line.
|
|
99
|
+
* A code-shaped token between the URL and the preamble cannot bind. A code-shaped
|
|
100
|
+
* token that shares the preamble line cannot bind. A code-shaped token inside a
|
|
101
|
+
* line that also holds other text cannot bind. Returns null when the preamble is
|
|
102
|
+
* absent, when the preamble has no line break after it, or when the first
|
|
103
|
+
* non-blank line after the preamble line is not a code line.
|
|
104
|
+
*/
|
|
105
|
+
function findShortCode(text, fromIndex) {
|
|
106
|
+
const preambleWindow = text.slice(fromIndex, fromIndex + MAX_URL_TO_PREAMBLE_GAP);
|
|
107
|
+
const preambleMatch = CODE_PREAMBLE_RE.exec(preambleWindow);
|
|
108
|
+
if (!preambleMatch)
|
|
109
|
+
return null;
|
|
110
|
+
// Advance to the end of the preamble line. The code sits on the next line, so
|
|
111
|
+
// the parser reads the code only after the preamble line ends. A code-shaped
|
|
112
|
+
// token on the preamble line cannot bind.
|
|
113
|
+
const preambleEnd = fromIndex + preambleMatch.index + preambleMatch[0].length;
|
|
114
|
+
const lineBreak = text.indexOf("\n", preambleEnd);
|
|
115
|
+
if (lineBreak === -1)
|
|
116
|
+
return null;
|
|
117
|
+
const codeWindow = text.slice(lineBreak + 1, lineBreak + 1 + MAX_PREAMBLE_TO_CODE_GAP);
|
|
118
|
+
// Read the first non-blank line after the preamble line. It must hold only the
|
|
119
|
+
// code. So the parser binds the code to the dedicated code line, and it rejects
|
|
120
|
+
// a line that mixes the code with other text.
|
|
121
|
+
for (const line of codeWindow.split("\n")) {
|
|
122
|
+
const trimmed = line.trim();
|
|
123
|
+
if (trimmed.length === 0)
|
|
124
|
+
continue;
|
|
125
|
+
const match = CODE_LINE_RE.exec(trimmed);
|
|
126
|
+
return match ? match[1] : null;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Parses Codex device-login output. Removes ANSI color sequences first, so
|
|
132
|
+
* colored output from Codex CLI 0.128.0 and later reads the same as plain
|
|
133
|
+
* output. The parser reads the URL first, then finds the "one-time code"
|
|
134
|
+
* preamble after the URL, then reads the code from the dedicated code line right
|
|
135
|
+
* after the preamble line. So the code binds to the prompt line that introduces
|
|
136
|
+
* it, and a code-shaped token between the URL and the preamble, on the preamble
|
|
137
|
+
* line, or mixed with other text on a later line, cannot form a prompt. Returns
|
|
138
|
+
* the login URL and the one-time code when both are present and valid. Returns
|
|
139
|
+
* null for any other input, including a non-string input, an absent prompt, a URL
|
|
140
|
+
* with a query or a fragment, a wrong origin or path, and a malformed short code.
|
|
141
|
+
* Never throws on input, and never puts the URL or the code into a log or an error.
|
|
142
|
+
*/
|
|
143
|
+
export function parseDeviceLoginPrompt(text) {
|
|
144
|
+
if (typeof text !== "string" || text.length === 0)
|
|
145
|
+
return null;
|
|
146
|
+
const clean = text.replace(ANSI_CSI_RE, "");
|
|
147
|
+
const urlMatch = findExactDeviceUrl(clean);
|
|
148
|
+
if (!urlMatch)
|
|
149
|
+
return null;
|
|
150
|
+
const code = findShortCode(clean, urlMatch.end);
|
|
151
|
+
if (!code)
|
|
152
|
+
return null;
|
|
153
|
+
return { url: urlMatch.url, code };
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=device-login-parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-login-parse.js","sourceRoot":"","sources":["../../src/server/device-login-parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-login-parse.test.d.ts","sourceRoot":"","sources":["../../src/server/device-login-parse.test.ts"],"names":[],"mappings":""}
|