@annabest/ticket-ai 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.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @annabest/ticket-ai
2
+
3
+ CLI for bootstrapping Codex on a Linux server against a Ticket AI OpenAI-compatible endpoint.
4
+
5
+ ## What it does
6
+
7
+ Running `codex-config` creates:
8
+
9
+ - `~/.codex/config.toml`
10
+ - `~/.codex/auth.json`
11
+
12
+ The generated `config.toml` uses:
13
+
14
+ - provider: `OpenAI`
15
+ - model: `gpt-5.4`
16
+ - base URL: `http://151.242.85.111:8080`
17
+ - wire API: `responses`
18
+
19
+ The command prompts the user for an API key, then writes:
20
+
21
+ ```json
22
+ {
23
+ "OPENAI_API_KEY": "sk-xxxx"
24
+ }
25
+ ```
26
+
27
+ ## Remote server usage
28
+
29
+ After this package is published to npm, users only need:
30
+
31
+ ```bash
32
+ sudo npm install -g @annabest/ticket-ai
33
+ codex-config
34
+ ```
35
+
36
+ Then they paste their key once.
37
+
38
+ ## Local test before publish
39
+
40
+ ```bash
41
+ npm pack
42
+ npm install -g ./annabest-ticket-ai-0.1.0.tgz
43
+ codex-config
44
+ ```
45
+
46
+ ## Non-interactive usage
47
+
48
+ ```bash
49
+ codex-config --api-key sk-xxxx
50
+ ```
51
+
52
+ ## Publish
53
+
54
+ ```bash
55
+ npm login
56
+ npm publish --access=public
57
+ ```
58
+
59
+ If the scoped package name is unavailable, change the scope or package name in `package.json` and publish again.
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const os = require("os");
5
+ const path = require("path");
6
+ const readline = require("readline/promises");
7
+ const { stdin, stdout } = require("process");
8
+
9
+ const DEFAULTS = {
10
+ providerName: "OpenAI",
11
+ baseUrl: "http://151.242.85.111:8080",
12
+ model: "gpt-5.4",
13
+ reviewModel: "gpt-5.4",
14
+ reasoningEffort: "xhigh",
15
+ contextWindow: 1000000,
16
+ compactTokenLimit: 900000
17
+ };
18
+
19
+ function usage() {
20
+ stdout.write(
21
+ [
22
+ "Usage: codex-config [--api-key <key>] [--force]",
23
+ "",
24
+ "Creates ~/.codex/config.toml and ~/.codex/auth.json for the Sub2API endpoint."
25
+ ].join("\n") + "\n"
26
+ );
27
+ }
28
+
29
+ function parseArgs(argv) {
30
+ const args = {
31
+ apiKey: process.env.OPENAI_API_KEY || "",
32
+ force: false
33
+ };
34
+
35
+ for (let i = 0; i < argv.length; i += 1) {
36
+ const arg = argv[i];
37
+ if (arg === "--help" || arg === "-h") {
38
+ usage();
39
+ process.exit(0);
40
+ }
41
+ if (arg === "--force" || arg === "-f") {
42
+ args.force = true;
43
+ continue;
44
+ }
45
+ if (arg === "--api-key") {
46
+ const next = argv[i + 1];
47
+ if (!next) {
48
+ throw new Error("--api-key requires a value");
49
+ }
50
+ args.apiKey = next.trim();
51
+ i += 1;
52
+ continue;
53
+ }
54
+ throw new Error(`Unknown argument: ${arg}`);
55
+ }
56
+
57
+ return args;
58
+ }
59
+
60
+ function ensureDir(dirPath) {
61
+ fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 });
62
+ }
63
+
64
+ function timestampForBackup() {
65
+ return new Date().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
66
+ }
67
+
68
+ function backupIfExists(filePath) {
69
+ if (!fs.existsSync(filePath)) {
70
+ return null;
71
+ }
72
+ const backupPath = `${filePath}.bak.${timestampForBackup()}`;
73
+ fs.copyFileSync(filePath, backupPath);
74
+ return backupPath;
75
+ }
76
+
77
+ function buildConfigToml() {
78
+ return [
79
+ `model_provider = "${DEFAULTS.providerName}"`,
80
+ `model = "${DEFAULTS.model}"`,
81
+ `review_model = "${DEFAULTS.reviewModel}"`,
82
+ `model_reasoning_effort = "${DEFAULTS.reasoningEffort}"`,
83
+ "disable_response_storage = true",
84
+ 'network_access = "enabled"',
85
+ "windows_wsl_setup_acknowledged = true",
86
+ `model_context_window = ${DEFAULTS.contextWindow}`,
87
+ `model_auto_compact_token_limit = ${DEFAULTS.compactTokenLimit}`,
88
+ "",
89
+ `[model_providers.${DEFAULTS.providerName}]`,
90
+ `name = "${DEFAULTS.providerName}"`,
91
+ `base_url = "${DEFAULTS.baseUrl}"`,
92
+ 'wire_api = "responses"',
93
+ "requires_openai_auth = true",
94
+ ""
95
+ ].join("\n");
96
+ }
97
+
98
+ function buildAuthJson(apiKey) {
99
+ return `${JSON.stringify({ OPENAI_API_KEY: apiKey }, null, 2)}\n`;
100
+ }
101
+
102
+ async function promptForApiKey() {
103
+ const rl = readline.createInterface({
104
+ input: stdin,
105
+ output: stdout
106
+ });
107
+
108
+ try {
109
+ stdout.write("Paste your OpenAI API key for Codex access: ");
110
+ const value = (await rl.question("")).trim();
111
+ if (!value) {
112
+ throw new Error("API key cannot be empty");
113
+ }
114
+ return value;
115
+ } finally {
116
+ rl.close();
117
+ }
118
+ }
119
+
120
+ function writeFile(filePath, content, mode) {
121
+ fs.writeFileSync(filePath, content, { encoding: "utf8", mode });
122
+ fs.chmodSync(filePath, mode);
123
+ }
124
+
125
+ async function main() {
126
+ const args = parseArgs(process.argv.slice(2));
127
+ const codexDir = path.join(os.homedir(), ".codex");
128
+ const configPath = path.join(codexDir, "config.toml");
129
+ const authPath = path.join(codexDir, "auth.json");
130
+
131
+ const apiKey = args.apiKey || (await promptForApiKey());
132
+ ensureDir(codexDir);
133
+
134
+ const backups = [];
135
+ if (!args.force) {
136
+ const configBackup = backupIfExists(configPath);
137
+ const authBackup = backupIfExists(authPath);
138
+ if (configBackup) {
139
+ backups.push(configBackup);
140
+ }
141
+ if (authBackup) {
142
+ backups.push(authBackup);
143
+ }
144
+ }
145
+
146
+ writeFile(configPath, buildConfigToml(), 0o600);
147
+ writeFile(authPath, buildAuthJson(apiKey), 0o600);
148
+
149
+ stdout.write(`Created ${configPath}\n`);
150
+ stdout.write(`Created ${authPath}\n`);
151
+ if (backups.length > 0) {
152
+ stdout.write(`Backups:\n${backups.map((item) => `- ${item}`).join("\n")}\n`);
153
+ }
154
+ stdout.write("Codex configuration completed.\n");
155
+ }
156
+
157
+ main().catch((error) => {
158
+ console.error(`codex-config failed: ${error.message}`);
159
+ process.exit(1);
160
+ });
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@annabest/ticket-ai",
3
+ "version": "0.1.0",
4
+ "description": "Bootstrap Codex config for a Sub2API OpenAI-compatible endpoint.",
5
+ "license": "MIT",
6
+ "files": [
7
+ "bin",
8
+ "README.md"
9
+ ],
10
+ "bin": {
11
+ "codex-config": "bin/codex-config.js"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "keywords": [
17
+ "codex",
18
+ "openai",
19
+ "sub2api",
20
+ "config",
21
+ "cli"
22
+ ],
23
+ "engines": {
24
+ "node": ">=18"
25
+ }
26
+ }