workon 3.2.2 → 3.2.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.
- package/dist/cli.js +172 -46
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +132 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -1
- package/dist/index.d.ts +35 -1
- package/dist/index.js +132 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -135,17 +135,51 @@ interface OpenOptions extends GlobalOptions {
|
|
|
135
135
|
dryRun?: boolean;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Config class with singleton pattern and file locking to prevent
|
|
140
|
+
* race conditions that could clear the config.
|
|
141
|
+
*/
|
|
138
142
|
declare class Config {
|
|
143
|
+
private static _instance;
|
|
139
144
|
private _transient;
|
|
140
145
|
private _store;
|
|
146
|
+
private _lock;
|
|
141
147
|
constructor();
|
|
148
|
+
/**
|
|
149
|
+
* Get the singleton instance (creates one if needed)
|
|
150
|
+
*/
|
|
151
|
+
static getInstance(): Config;
|
|
152
|
+
/**
|
|
153
|
+
* Reset the singleton instance (for testing purposes)
|
|
154
|
+
*/
|
|
155
|
+
static resetInstance(): void;
|
|
142
156
|
get<T = unknown>(key: string, defaultValue?: T): T | undefined;
|
|
143
|
-
set(key: string, value
|
|
157
|
+
set(key: string, value: unknown): void;
|
|
144
158
|
has(key: string): boolean;
|
|
145
159
|
delete(key: string): void;
|
|
160
|
+
/**
|
|
161
|
+
* Get all projects. Returns a fresh copy from the store.
|
|
162
|
+
*/
|
|
146
163
|
getProjects(): Record<string, ProjectConfig>;
|
|
147
164
|
getProject(name: string): ProjectConfig | undefined;
|
|
165
|
+
/**
|
|
166
|
+
* Set a project with file locking to prevent race conditions.
|
|
167
|
+
* This ensures atomic read-modify-write operations.
|
|
168
|
+
*/
|
|
169
|
+
setProjectSafe(name: string, config: ProjectConfig): Promise<void>;
|
|
170
|
+
/**
|
|
171
|
+
* Synchronous version of setProject for backwards compatibility.
|
|
172
|
+
* Note: This is less safe than setProjectSafe() in concurrent scenarios.
|
|
173
|
+
* Consider migrating to setProjectSafe() for critical operations.
|
|
174
|
+
*/
|
|
148
175
|
setProject(name: string, config: ProjectConfig): void;
|
|
176
|
+
/**
|
|
177
|
+
* Delete a project with file locking to prevent race conditions.
|
|
178
|
+
*/
|
|
179
|
+
deleteProjectSafe(name: string): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Synchronous version of deleteProject for backwards compatibility.
|
|
182
|
+
*/
|
|
149
183
|
deleteProject(name: string): void;
|
|
150
184
|
getDefaults(): ProjectDefaults | undefined;
|
|
151
185
|
setDefaults(defaults: ProjectDefaults): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -135,17 +135,51 @@ interface OpenOptions extends GlobalOptions {
|
|
|
135
135
|
dryRun?: boolean;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Config class with singleton pattern and file locking to prevent
|
|
140
|
+
* race conditions that could clear the config.
|
|
141
|
+
*/
|
|
138
142
|
declare class Config {
|
|
143
|
+
private static _instance;
|
|
139
144
|
private _transient;
|
|
140
145
|
private _store;
|
|
146
|
+
private _lock;
|
|
141
147
|
constructor();
|
|
148
|
+
/**
|
|
149
|
+
* Get the singleton instance (creates one if needed)
|
|
150
|
+
*/
|
|
151
|
+
static getInstance(): Config;
|
|
152
|
+
/**
|
|
153
|
+
* Reset the singleton instance (for testing purposes)
|
|
154
|
+
*/
|
|
155
|
+
static resetInstance(): void;
|
|
142
156
|
get<T = unknown>(key: string, defaultValue?: T): T | undefined;
|
|
143
|
-
set(key: string, value
|
|
157
|
+
set(key: string, value: unknown): void;
|
|
144
158
|
has(key: string): boolean;
|
|
145
159
|
delete(key: string): void;
|
|
160
|
+
/**
|
|
161
|
+
* Get all projects. Returns a fresh copy from the store.
|
|
162
|
+
*/
|
|
146
163
|
getProjects(): Record<string, ProjectConfig>;
|
|
147
164
|
getProject(name: string): ProjectConfig | undefined;
|
|
165
|
+
/**
|
|
166
|
+
* Set a project with file locking to prevent race conditions.
|
|
167
|
+
* This ensures atomic read-modify-write operations.
|
|
168
|
+
*/
|
|
169
|
+
setProjectSafe(name: string, config: ProjectConfig): Promise<void>;
|
|
170
|
+
/**
|
|
171
|
+
* Synchronous version of setProject for backwards compatibility.
|
|
172
|
+
* Note: This is less safe than setProjectSafe() in concurrent scenarios.
|
|
173
|
+
* Consider migrating to setProjectSafe() for critical operations.
|
|
174
|
+
*/
|
|
148
175
|
setProject(name: string, config: ProjectConfig): void;
|
|
176
|
+
/**
|
|
177
|
+
* Delete a project with file locking to prevent race conditions.
|
|
178
|
+
*/
|
|
179
|
+
deleteProjectSafe(name: string): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Synchronous version of deleteProject for backwards compatibility.
|
|
182
|
+
*/
|
|
149
183
|
deleteProject(name: string): void;
|
|
150
184
|
getDefaults(): ProjectDefaults | undefined;
|
|
151
185
|
setDefaults(defaults: ProjectDefaults): void;
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,94 @@
|
|
|
1
1
|
// src/lib/config.ts
|
|
2
2
|
import Conf from "conf";
|
|
3
|
+
import { openSync, closeSync, unlinkSync, existsSync, mkdirSync } from "fs";
|
|
4
|
+
import { dirname } from "path";
|
|
3
5
|
var TRANSIENT_PROPS = ["pkg", "work"];
|
|
4
|
-
var
|
|
6
|
+
var FileLock = class _FileLock {
|
|
7
|
+
lockPath;
|
|
8
|
+
fd = null;
|
|
9
|
+
static LOCK_TIMEOUT_MS = 5e3;
|
|
10
|
+
static RETRY_INTERVAL_MS = 50;
|
|
11
|
+
constructor(configPath) {
|
|
12
|
+
this.lockPath = `${configPath}.lock`;
|
|
13
|
+
}
|
|
14
|
+
async acquire() {
|
|
15
|
+
const startTime = Date.now();
|
|
16
|
+
const lockDir = dirname(this.lockPath);
|
|
17
|
+
if (!existsSync(lockDir)) {
|
|
18
|
+
mkdirSync(lockDir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
while (Date.now() - startTime < _FileLock.LOCK_TIMEOUT_MS) {
|
|
21
|
+
try {
|
|
22
|
+
this.fd = openSync(this.lockPath, "wx");
|
|
23
|
+
return;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (error.code === "EEXIST") {
|
|
26
|
+
try {
|
|
27
|
+
const stat = await import("fs").then((fs) => fs.promises.stat(this.lockPath));
|
|
28
|
+
const age = Date.now() - stat.mtimeMs;
|
|
29
|
+
if (age > _FileLock.LOCK_TIMEOUT_MS) {
|
|
30
|
+
try {
|
|
31
|
+
unlinkSync(this.lockPath);
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
await new Promise((resolve) => setTimeout(resolve, _FileLock.RETRY_INTERVAL_MS));
|
|
38
|
+
} else {
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
throw new Error("Failed to acquire config lock: timeout");
|
|
44
|
+
}
|
|
45
|
+
release() {
|
|
46
|
+
if (this.fd !== null) {
|
|
47
|
+
try {
|
|
48
|
+
closeSync(this.fd);
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
this.fd = null;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
unlinkSync(this.lockPath);
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var Config = class _Config {
|
|
60
|
+
static _instance = null;
|
|
5
61
|
_transient = {};
|
|
62
|
+
// Using definite assignment assertion since singleton pattern may return existing instance
|
|
6
63
|
_store;
|
|
64
|
+
_lock;
|
|
7
65
|
constructor() {
|
|
66
|
+
if (_Config._instance && process.env.NODE_ENV !== "test") {
|
|
67
|
+
return _Config._instance;
|
|
68
|
+
}
|
|
8
69
|
this._store = new Conf({
|
|
9
|
-
projectName: "workon"
|
|
70
|
+
projectName: "workon",
|
|
71
|
+
...process.env.WORKON_CONFIG_DIR && { cwd: process.env.WORKON_CONFIG_DIR }
|
|
10
72
|
});
|
|
73
|
+
this._lock = new FileLock(this._store.path);
|
|
74
|
+
if (process.env.NODE_ENV !== "test") {
|
|
75
|
+
_Config._instance = this;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get the singleton instance (creates one if needed)
|
|
80
|
+
*/
|
|
81
|
+
static getInstance() {
|
|
82
|
+
if (!_Config._instance) {
|
|
83
|
+
_Config._instance = new _Config();
|
|
84
|
+
}
|
|
85
|
+
return _Config._instance;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Reset the singleton instance (for testing purposes)
|
|
89
|
+
*/
|
|
90
|
+
static resetInstance() {
|
|
91
|
+
_Config._instance = null;
|
|
11
92
|
}
|
|
12
93
|
get(key, defaultValue) {
|
|
13
94
|
const rootKey = key.split(".")[0];
|
|
@@ -22,10 +103,9 @@ var Config = class {
|
|
|
22
103
|
this._transient[key] = value;
|
|
23
104
|
} else {
|
|
24
105
|
if (value === void 0) {
|
|
25
|
-
|
|
26
|
-
} else {
|
|
27
|
-
this._store.set(key, value);
|
|
106
|
+
throw new Error(`Cannot set '${key}' to undefined. Use delete() to remove keys.`);
|
|
28
107
|
}
|
|
108
|
+
this._store.set(key, value);
|
|
29
109
|
}
|
|
30
110
|
}
|
|
31
111
|
has(key) {
|
|
@@ -43,6 +123,9 @@ var Config = class {
|
|
|
43
123
|
this._store.delete(key);
|
|
44
124
|
}
|
|
45
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Get all projects. Returns a fresh copy from the store.
|
|
128
|
+
*/
|
|
46
129
|
getProjects() {
|
|
47
130
|
return this.get("projects") ?? {};
|
|
48
131
|
}
|
|
@@ -50,15 +133,50 @@ var Config = class {
|
|
|
50
133
|
const projects = this.getProjects();
|
|
51
134
|
return projects[name];
|
|
52
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Set a project with file locking to prevent race conditions.
|
|
138
|
+
* This ensures atomic read-modify-write operations.
|
|
139
|
+
*/
|
|
140
|
+
async setProjectSafe(name, config) {
|
|
141
|
+
await this._lock.acquire();
|
|
142
|
+
try {
|
|
143
|
+
const freshProjects = this._store.get("projects") ?? {};
|
|
144
|
+
freshProjects[name] = config;
|
|
145
|
+
this._store.set("projects", freshProjects);
|
|
146
|
+
} finally {
|
|
147
|
+
this._lock.release();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Synchronous version of setProject for backwards compatibility.
|
|
152
|
+
* Note: This is less safe than setProjectSafe() in concurrent scenarios.
|
|
153
|
+
* Consider migrating to setProjectSafe() for critical operations.
|
|
154
|
+
*/
|
|
53
155
|
setProject(name, config) {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
this.set("projects",
|
|
156
|
+
const freshProjects = this._store.get("projects") ?? {};
|
|
157
|
+
freshProjects[name] = config;
|
|
158
|
+
this._store.set("projects", freshProjects);
|
|
57
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Delete a project with file locking to prevent race conditions.
|
|
162
|
+
*/
|
|
163
|
+
async deleteProjectSafe(name) {
|
|
164
|
+
await this._lock.acquire();
|
|
165
|
+
try {
|
|
166
|
+
const freshProjects = this._store.get("projects") ?? {};
|
|
167
|
+
delete freshProjects[name];
|
|
168
|
+
this._store.set("projects", freshProjects);
|
|
169
|
+
} finally {
|
|
170
|
+
this._lock.release();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Synchronous version of deleteProject for backwards compatibility.
|
|
175
|
+
*/
|
|
58
176
|
deleteProject(name) {
|
|
59
|
-
const
|
|
60
|
-
delete
|
|
61
|
-
this.set("projects",
|
|
177
|
+
const freshProjects = this._store.get("projects") ?? {};
|
|
178
|
+
delete freshProjects[name];
|
|
179
|
+
this._store.set("projects", freshProjects);
|
|
62
180
|
}
|
|
63
181
|
getDefaults() {
|
|
64
182
|
return this.get("project_defaults");
|
|
@@ -441,7 +559,7 @@ var EnvironmentRecognizer = class {
|
|
|
441
559
|
}
|
|
442
560
|
static ensureConfigured() {
|
|
443
561
|
if (!this.configured) {
|
|
444
|
-
this.config =
|
|
562
|
+
this.config = Config.getInstance();
|
|
445
563
|
this.log = {
|
|
446
564
|
debug: () => {
|
|
447
565
|
},
|
|
@@ -769,10 +887,10 @@ var ClaudeEvent = class _ClaudeEvent {
|
|
|
769
887
|
}
|
|
770
888
|
static getClaudeCommand(config) {
|
|
771
889
|
if (typeof config === "boolean" || config === void 0) {
|
|
772
|
-
return "claude";
|
|
890
|
+
return "claude --dangerously-skip-permissions";
|
|
773
891
|
}
|
|
774
892
|
const flags = config.flags || [];
|
|
775
|
-
return flags.length > 0 ? `claude ${flags.join(" ")}` : "claude";
|
|
893
|
+
return flags.length > 0 ? `claude --dangerously-skip-permissions ${flags.join(" ")}` : "claude --dangerously-skip-permissions";
|
|
776
894
|
}
|
|
777
895
|
static get processing() {
|
|
778
896
|
return {
|