@4pm/cli 0.7.0 → 0.8.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/dist/index.js +656 -156
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1353,7 +1353,7 @@ var require_react_development = __commonJS({
|
|
|
1353
1353
|
var dispatcher = resolveDispatcher();
|
|
1354
1354
|
return dispatcher.useReducer(reducer, initialArg, init);
|
|
1355
1355
|
}
|
|
1356
|
-
function
|
|
1356
|
+
function useRef2(initialValue) {
|
|
1357
1357
|
var dispatcher = resolveDispatcher();
|
|
1358
1358
|
return dispatcher.useRef(initialValue);
|
|
1359
1359
|
}
|
|
@@ -1369,7 +1369,7 @@ var require_react_development = __commonJS({
|
|
|
1369
1369
|
var dispatcher = resolveDispatcher();
|
|
1370
1370
|
return dispatcher.useLayoutEffect(create2, deps);
|
|
1371
1371
|
}
|
|
1372
|
-
function
|
|
1372
|
+
function useCallback2(callback, deps) {
|
|
1373
1373
|
var dispatcher = resolveDispatcher();
|
|
1374
1374
|
return dispatcher.useCallback(callback, deps);
|
|
1375
1375
|
}
|
|
@@ -1995,14 +1995,14 @@ var require_react_development = __commonJS({
|
|
|
1995
1995
|
var thenableResult = result;
|
|
1996
1996
|
var wasAwaited = false;
|
|
1997
1997
|
var thenable = {
|
|
1998
|
-
then: function(
|
|
1998
|
+
then: function(resolve7, reject) {
|
|
1999
1999
|
wasAwaited = true;
|
|
2000
2000
|
thenableResult.then(function(returnValue2) {
|
|
2001
2001
|
popActScope(prevActScopeDepth);
|
|
2002
2002
|
if (actScopeDepth === 0) {
|
|
2003
|
-
recursivelyFlushAsyncActWork(returnValue2,
|
|
2003
|
+
recursivelyFlushAsyncActWork(returnValue2, resolve7, reject);
|
|
2004
2004
|
} else {
|
|
2005
|
-
|
|
2005
|
+
resolve7(returnValue2);
|
|
2006
2006
|
}
|
|
2007
2007
|
}, function(error2) {
|
|
2008
2008
|
popActScope(prevActScopeDepth);
|
|
@@ -2032,20 +2032,20 @@ var require_react_development = __commonJS({
|
|
|
2032
2032
|
ReactCurrentActQueue.current = null;
|
|
2033
2033
|
}
|
|
2034
2034
|
var _thenable = {
|
|
2035
|
-
then: function(
|
|
2035
|
+
then: function(resolve7, reject) {
|
|
2036
2036
|
if (ReactCurrentActQueue.current === null) {
|
|
2037
2037
|
ReactCurrentActQueue.current = [];
|
|
2038
|
-
recursivelyFlushAsyncActWork(returnValue,
|
|
2038
|
+
recursivelyFlushAsyncActWork(returnValue, resolve7, reject);
|
|
2039
2039
|
} else {
|
|
2040
|
-
|
|
2040
|
+
resolve7(returnValue);
|
|
2041
2041
|
}
|
|
2042
2042
|
}
|
|
2043
2043
|
};
|
|
2044
2044
|
return _thenable;
|
|
2045
2045
|
} else {
|
|
2046
2046
|
var _thenable2 = {
|
|
2047
|
-
then: function(
|
|
2048
|
-
|
|
2047
|
+
then: function(resolve7, reject) {
|
|
2048
|
+
resolve7(returnValue);
|
|
2049
2049
|
}
|
|
2050
2050
|
};
|
|
2051
2051
|
return _thenable2;
|
|
@@ -2061,7 +2061,7 @@ var require_react_development = __commonJS({
|
|
|
2061
2061
|
actScopeDepth = prevActScopeDepth;
|
|
2062
2062
|
}
|
|
2063
2063
|
}
|
|
2064
|
-
function recursivelyFlushAsyncActWork(returnValue,
|
|
2064
|
+
function recursivelyFlushAsyncActWork(returnValue, resolve7, reject) {
|
|
2065
2065
|
{
|
|
2066
2066
|
var queue = ReactCurrentActQueue.current;
|
|
2067
2067
|
if (queue !== null) {
|
|
@@ -2070,16 +2070,16 @@ var require_react_development = __commonJS({
|
|
|
2070
2070
|
enqueueTask(function() {
|
|
2071
2071
|
if (queue.length === 0) {
|
|
2072
2072
|
ReactCurrentActQueue.current = null;
|
|
2073
|
-
|
|
2073
|
+
resolve7(returnValue);
|
|
2074
2074
|
} else {
|
|
2075
|
-
recursivelyFlushAsyncActWork(returnValue,
|
|
2075
|
+
recursivelyFlushAsyncActWork(returnValue, resolve7, reject);
|
|
2076
2076
|
}
|
|
2077
2077
|
});
|
|
2078
2078
|
} catch (error2) {
|
|
2079
2079
|
reject(error2);
|
|
2080
2080
|
}
|
|
2081
2081
|
} else {
|
|
2082
|
-
|
|
2082
|
+
resolve7(returnValue);
|
|
2083
2083
|
}
|
|
2084
2084
|
}
|
|
2085
2085
|
}
|
|
@@ -2136,7 +2136,7 @@ var require_react_development = __commonJS({
|
|
|
2136
2136
|
exports.memo = memo;
|
|
2137
2137
|
exports.startTransition = startTransition;
|
|
2138
2138
|
exports.unstable_act = act;
|
|
2139
|
-
exports.useCallback =
|
|
2139
|
+
exports.useCallback = useCallback2;
|
|
2140
2140
|
exports.useContext = useContext7;
|
|
2141
2141
|
exports.useDebugValue = useDebugValue;
|
|
2142
2142
|
exports.useDeferredValue = useDeferredValue;
|
|
@@ -2147,7 +2147,7 @@ var require_react_development = __commonJS({
|
|
|
2147
2147
|
exports.useLayoutEffect = useLayoutEffect2;
|
|
2148
2148
|
exports.useMemo = useMemo4;
|
|
2149
2149
|
exports.useReducer = useReducer;
|
|
2150
|
-
exports.useRef =
|
|
2150
|
+
exports.useRef = useRef2;
|
|
2151
2151
|
exports.useState = useState6;
|
|
2152
2152
|
exports.useSyncExternalStore = useSyncExternalStore;
|
|
2153
2153
|
exports.useTransition = useTransition;
|
|
@@ -24482,7 +24482,8 @@ function defaultProfileConfig() {
|
|
|
24482
24482
|
codexHome: [{ profile: ".codex", args: ["exec"], model: "" }],
|
|
24483
24483
|
antigravityHome: [],
|
|
24484
24484
|
autoUpdate: true,
|
|
24485
|
-
commandHistoryUploadMinutes: 10
|
|
24485
|
+
commandHistoryUploadMinutes: 10,
|
|
24486
|
+
autoClearIdleMinutes: 10
|
|
24486
24487
|
};
|
|
24487
24488
|
}
|
|
24488
24489
|
function ensureProfileConfig(dir2) {
|
|
@@ -24690,8 +24691,8 @@ async function runLink(serverUrl, explicitProfile) {
|
|
|
24690
24691
|
}
|
|
24691
24692
|
|
|
24692
24693
|
// src/commands/start.ts
|
|
24693
|
-
import { spawn as
|
|
24694
|
-
import { join as
|
|
24694
|
+
import { spawn as spawn5 } from "child_process";
|
|
24695
|
+
import { join as join19 } from "path";
|
|
24695
24696
|
|
|
24696
24697
|
// src/core/update.ts
|
|
24697
24698
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -24862,7 +24863,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
|
|
|
24862
24863
|
|
|
24863
24864
|
// src/core/ws-client.ts
|
|
24864
24865
|
import { randomUUID } from "crypto";
|
|
24865
|
-
import { basename as basename3, join as
|
|
24866
|
+
import { basename as basename3, join as join15 } from "path";
|
|
24866
24867
|
|
|
24867
24868
|
// ../../22-packages/11-shared/51-utils/src/helpers.ts
|
|
24868
24869
|
function backoffJitterMs(attempt, baseMs = 500, maxMs = 1e4) {
|
|
@@ -24965,6 +24966,12 @@ var PermissionCode = {
|
|
|
24965
24966
|
PROJECT_AI_ASSIST: "project.ai_assist",
|
|
24966
24967
|
PROJECT_MEMO_READ: "project.memo_read",
|
|
24967
24968
|
PROJECT_MEMO_WRITE: "project.memo_write",
|
|
24969
|
+
// Git tab (ADR-0151): read git (branches/graph/log/status/diff) via command dispatch;
|
|
24970
|
+
// write = commit/push/pull/checkout/merge/PR + fs.write (machine-0027)
|
|
24971
|
+
PROJECT_GIT: "project.git",
|
|
24972
|
+
PROJECT_GIT_WRITE: "project.git_write",
|
|
24973
|
+
// Autonomous mode (ADR-0152): settings/start-pause/VERIFY-approve/post-USER_TODO
|
|
24974
|
+
PROJECT_AUTONOMOUS: "project.autonomous",
|
|
24968
24975
|
// permission
|
|
24969
24976
|
PERMISSION_LIST: "permission.list",
|
|
24970
24977
|
PERMISSION_READ: "permission.read",
|
|
@@ -25064,6 +25071,9 @@ var ROLE_DEFAULT_PERMISSIONS = {
|
|
|
25064
25071
|
P.PROJECT_AI_ASSIST,
|
|
25065
25072
|
P.PROJECT_MEMO_READ,
|
|
25066
25073
|
P.PROJECT_MEMO_WRITE,
|
|
25074
|
+
P.PROJECT_GIT,
|
|
25075
|
+
P.PROJECT_GIT_WRITE,
|
|
25076
|
+
P.PROJECT_AUTONOMOUS,
|
|
25067
25077
|
P.PERMISSION_LIST,
|
|
25068
25078
|
P.PERMISSION_READ,
|
|
25069
25079
|
P.MACHINE_PAIR,
|
|
@@ -25088,6 +25098,7 @@ var ROLE_DEFAULT_PERMISSIONS = {
|
|
|
25088
25098
|
P.PROJECT_READ,
|
|
25089
25099
|
P.PROJECT_MEMO_READ,
|
|
25090
25100
|
P.PROJECT_MEMO_WRITE,
|
|
25101
|
+
P.PROJECT_GIT,
|
|
25091
25102
|
P.INTEGRATION_READ
|
|
25092
25103
|
],
|
|
25093
25104
|
[Role.QA]: [
|
|
@@ -25098,6 +25109,7 @@ var ROLE_DEFAULT_PERMISSIONS = {
|
|
|
25098
25109
|
P.PROJECT_READ,
|
|
25099
25110
|
P.PROJECT_MEMO_READ,
|
|
25100
25111
|
P.PROJECT_MEMO_WRITE,
|
|
25112
|
+
P.PROJECT_GIT,
|
|
25101
25113
|
P.INTEGRATION_READ
|
|
25102
25114
|
],
|
|
25103
25115
|
[Role.BA]: [
|
|
@@ -25108,6 +25120,7 @@ var ROLE_DEFAULT_PERMISSIONS = {
|
|
|
25108
25120
|
P.PROJECT_READ,
|
|
25109
25121
|
P.PROJECT_MEMO_READ,
|
|
25110
25122
|
P.PROJECT_MEMO_WRITE,
|
|
25123
|
+
P.PROJECT_GIT,
|
|
25111
25124
|
P.INTEGRATION_READ
|
|
25112
25125
|
],
|
|
25113
25126
|
[Role.MACHINE]: [
|
|
@@ -25115,6 +25128,11 @@ var ROLE_DEFAULT_PERMISSIONS = {
|
|
|
25115
25128
|
P.PROJECT_READ,
|
|
25116
25129
|
P.PROJECT_MEMO_READ,
|
|
25117
25130
|
P.PROJECT_MEMO_WRITE,
|
|
25131
|
+
// git read + write so the autonomous merge cycle (auto-cycle.md) can run (ADR-0151)
|
|
25132
|
+
P.PROJECT_GIT,
|
|
25133
|
+
P.PROJECT_GIT_WRITE,
|
|
25134
|
+
// autonomous mode control on its own physic project (ADR-0152)
|
|
25135
|
+
P.PROJECT_AUTONOMOUS,
|
|
25118
25136
|
// view one's own worker/cli + configure the physic project
|
|
25119
25137
|
P.MACHINE_LIST,
|
|
25120
25138
|
P.MACHINE_PHYSIC_MANAGE,
|
|
@@ -25142,7 +25160,10 @@ var PROJECT_SCOPE_CHECKED_CODES = [
|
|
|
25142
25160
|
P.PROJECT_PAUSE,
|
|
25143
25161
|
P.PROJECT_CLOSE,
|
|
25144
25162
|
P.PROJECT_PAUSE_MEMBER,
|
|
25145
|
-
P.PROJECT_AI_ASSIST
|
|
25163
|
+
P.PROJECT_AI_ASSIST,
|
|
25164
|
+
P.PROJECT_GIT,
|
|
25165
|
+
P.PROJECT_GIT_WRITE,
|
|
25166
|
+
P.PROJECT_AUTONOMOUS
|
|
25146
25167
|
];
|
|
25147
25168
|
var SELF_SERVICE_CODES = [
|
|
25148
25169
|
P.AUTH_LOGOUT,
|
|
@@ -25182,6 +25203,9 @@ var TEAM_GRANTABLE_CODES = [
|
|
|
25182
25203
|
P.PROJECT_AI_ASSIST,
|
|
25183
25204
|
P.PROJECT_MEMO_READ,
|
|
25184
25205
|
P.PROJECT_MEMO_WRITE,
|
|
25206
|
+
P.PROJECT_GIT,
|
|
25207
|
+
P.PROJECT_GIT_WRITE,
|
|
25208
|
+
P.PROJECT_AUTONOMOUS,
|
|
25185
25209
|
P.COMMAND_EXECUTE,
|
|
25186
25210
|
P.COMMAND_READ,
|
|
25187
25211
|
P.MACHINE_LIST,
|
|
@@ -25203,6 +25227,9 @@ var PROJECT_GRANTABLE_CODES = [
|
|
|
25203
25227
|
P.PROJECT_AI_ASSIST,
|
|
25204
25228
|
P.PROJECT_MEMO_READ,
|
|
25205
25229
|
P.PROJECT_MEMO_WRITE,
|
|
25230
|
+
P.PROJECT_GIT,
|
|
25231
|
+
P.PROJECT_GIT_WRITE,
|
|
25232
|
+
P.PROJECT_AUTONOMOUS,
|
|
25206
25233
|
P.COMMAND_EXECUTE,
|
|
25207
25234
|
P.COMMAND_READ,
|
|
25208
25235
|
P.MACHINE_PHYSIC_MANAGE
|
|
@@ -29488,6 +29515,9 @@ var createProjectRequestSchema = external_exports.object({
|
|
|
29488
29515
|
description: external_exports.string().max(2e3).optional(),
|
|
29489
29516
|
machineUserId: external_exports.string().uuid().optional()
|
|
29490
29517
|
});
|
|
29518
|
+
var copyProjectRequestSchema = external_exports.object({
|
|
29519
|
+
name: external_exports.string().min(1).max(100)
|
|
29520
|
+
});
|
|
29491
29521
|
var DEFAULT_OUTBOUND_RULES = {
|
|
29492
29522
|
secret: [
|
|
29493
29523
|
String.raw`/\b(api[_-]?key|secret|password|passwd|access[_-]?token|bearer)\b\s*[:=]\s*\S+/i`,
|
|
@@ -29518,7 +29548,15 @@ var updateProjectRequestSchema = external_exports.object({
|
|
|
29518
29548
|
// Messenger retention in days (0 = keep forever) — manager only (ADR-0078).
|
|
29519
29549
|
messengerRetentionDays: external_exports.number().int().min(0).max(3650).optional(),
|
|
29520
29550
|
// Project settings (ADR-0081) — key-level shallow-merge; `tokens` clamped on read.
|
|
29521
|
-
settings: external_exports.record(external_exports.unknown()).optional()
|
|
29551
|
+
settings: external_exports.record(external_exports.unknown()).optional(),
|
|
29552
|
+
// Identity ↔ spec sync (ADR-0148): the Settings tab edits the spec basic group
|
|
29553
|
+
// (id = spec slug, name, desc). The server merges these into `spec` (READY) or
|
|
29554
|
+
// `specDraft` (DRAFT) so the Content → Spec tab / wizard stays consistent.
|
|
29555
|
+
specBasic: external_exports.object({
|
|
29556
|
+
id: external_exports.string().max(200),
|
|
29557
|
+
name: external_exports.string().max(100),
|
|
29558
|
+
desc: external_exports.string().max(2e3)
|
|
29559
|
+
}).partial().optional()
|
|
29522
29560
|
});
|
|
29523
29561
|
var MEMO_MAX_LENGTH = 65536;
|
|
29524
29562
|
var putMemoRequestSchema = external_exports.object({
|
|
@@ -29705,6 +29743,25 @@ var listMachineLinksQuerySchema = baseRequestSchema.extend({
|
|
|
29705
29743
|
var updateMachineLinkRequestSchema = external_exports.object({
|
|
29706
29744
|
hashcode3ExpiresAt: external_exports.string().datetime().nullable()
|
|
29707
29745
|
});
|
|
29746
|
+
var configWriteRequestSchema = external_exports.object({
|
|
29747
|
+
config: external_exports.string().max(256 * 1024)
|
|
29748
|
+
});
|
|
29749
|
+
var FILE_WRITE_MAX_BYTES = 2 * 1024 * 1024;
|
|
29750
|
+
var fsWriteRequestSchema = external_exports.object({
|
|
29751
|
+
/** Path relative to the physic-project root; a `..` escaping the root is rejected on the cli. */
|
|
29752
|
+
path: external_exports.string().min(1).max(1024),
|
|
29753
|
+
/** Full new UTF-8 content (replaces the file wholesale). */
|
|
29754
|
+
content: external_exports.string().max(FILE_WRITE_MAX_BYTES)
|
|
29755
|
+
});
|
|
29756
|
+
var autonomousWriteRequestSchema = external_exports.discriminatedUnion("kind", [
|
|
29757
|
+
external_exports.object({ kind: external_exports.literal("settings"), settings: external_exports.string().max(64 * 1024) }),
|
|
29758
|
+
external_exports.object({ kind: external_exports.literal("approvals"), taskId: external_exports.string().min(1).max(64), approved: external_exports.boolean() }),
|
|
29759
|
+
external_exports.object({ kind: external_exports.literal("userTodo"), content: external_exports.string().min(1).max(16 * 1024) }),
|
|
29760
|
+
external_exports.object({ kind: external_exports.literal("cron"), action: external_exports.enum(["install", "uninstall"]) })
|
|
29761
|
+
]);
|
|
29762
|
+
var autonomousLogsQuerySchema = external_exports.object({
|
|
29763
|
+
date: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
|
|
29764
|
+
});
|
|
29708
29765
|
|
|
29709
29766
|
// ../../22-packages/11-shared/31-dto/src/command.ts
|
|
29710
29767
|
var COMMAND_MAX_LEN = 8e3;
|
|
@@ -29725,7 +29782,14 @@ var dispatchCommandRequestSchema = external_exports.object({
|
|
|
29725
29782
|
* Short human label shown to OTHER tabs watching this cli's activity feed (ADR-0101):
|
|
29726
29783
|
* e.g. "AI review" so the Console shows a friendly line instead of the raw prompt.
|
|
29727
29784
|
*/
|
|
29728
|
-
label: external_exports.string().max(120).optional()
|
|
29785
|
+
label: external_exports.string().max(120).optional(),
|
|
29786
|
+
/**
|
|
29787
|
+
* Git tab (ADR-0151): when set, `command` is a `git`/`gh`/`glab` command run on the
|
|
29788
|
+
* worker for the Git tab. The server routes the permission to `project.git` (`read`) /
|
|
29789
|
+
* `project.git_write` (`write`) instead of `command.execute`, and validates `command`
|
|
29790
|
+
* against the git allowlist (`isGitCommandAllowed`). Absent ⇒ normal console dispatch.
|
|
29791
|
+
*/
|
|
29792
|
+
gitOp: external_exports.enum(["read", "write"]).optional()
|
|
29729
29793
|
}).superRefine((v, ctx) => {
|
|
29730
29794
|
if (!v.ai && v.command.length > COMMAND_MAX_LEN) {
|
|
29731
29795
|
ctx.addIssue({
|
|
@@ -29872,12 +29936,6 @@ var checkoutRequestSchema = external_exports.object({
|
|
|
29872
29936
|
planCode: external_exports.string().min(1).max(50),
|
|
29873
29937
|
provider: external_exports.enum([PaymentProvider.STRIPE, PaymentProvider.PAYPAL]).optional()
|
|
29874
29938
|
});
|
|
29875
|
-
var upsertPlanRequestSchema = external_exports.object({
|
|
29876
|
-
name: external_exports.string().min(1).max(100),
|
|
29877
|
-
price: external_exports.number().int().min(0).default(0),
|
|
29878
|
-
limits: external_exports.array(planLimitSchema).default([]),
|
|
29879
|
-
entitlements: planEntitlementsSchema.optional()
|
|
29880
|
-
});
|
|
29881
29939
|
var assignPlanRequestSchema = external_exports.object({
|
|
29882
29940
|
planCode: external_exports.string().min(1).max(50),
|
|
29883
29941
|
/** Cycle length in days (defaults to 30). */
|
|
@@ -29948,8 +30006,21 @@ var WsChannels = {
|
|
|
29948
30006
|
...WsChannel,
|
|
29949
30007
|
FS_LIST: "fs.list",
|
|
29950
30008
|
FS_READ: "fs.read",
|
|
30009
|
+
// server → cli (reply): write a file, path-clamped to the physic root (machine-0027,
|
|
30010
|
+
// ADR-0151) — the Git tab's manual conflict resolution (`project.git_write`); the only
|
|
30011
|
+
// git-tab write not carried by a git command over command dispatch.
|
|
30012
|
+
FS_WRITE: "fs.write",
|
|
29951
30013
|
GIT_DIFF: "git.diff",
|
|
29952
30014
|
GIT_ENV: "git.env",
|
|
30015
|
+
// server → cli (reply): read/write the paired profile's config.json (machine-0025/0026,
|
|
30016
|
+
// ADR-0141) — the machine-user Settings tab syncs the worker cli's config.
|
|
30017
|
+
CONFIG_READ: "config.read",
|
|
30018
|
+
CONFIG_WRITE: "config.write",
|
|
30019
|
+
// autonomous mode control (ADR-0152): read settings+status+books+approvals, tail logs, and
|
|
30020
|
+
// write {settings|approvals|userTodo|cron} — the dashboard drives the worker's autonomous engine.
|
|
30021
|
+
AUTONOMOUS_READ: "autonomous.read",
|
|
30022
|
+
AUTONOMOUS_WRITE: "autonomous.write",
|
|
30023
|
+
AUTONOMOUS_LOGS: "autonomous.logs",
|
|
29953
30024
|
// git history browse (read-through cli — ADR-0089): repos on the physic project,
|
|
29954
30025
|
// commit log, changed files of a commit, parent↔commit diff of one file.
|
|
29955
30026
|
GIT_REPOS: "git.repos",
|
|
@@ -29967,6 +30038,11 @@ var WsChannels = {
|
|
|
29967
30038
|
PHYSIC_DELETE: "physic.delete",
|
|
29968
30039
|
// command lifecycle
|
|
29969
30040
|
COMMAND_ANNOUNCE: "command.announce",
|
|
30041
|
+
// console transcript sync (ADR-0150): the cli streams its authoritative SessionBus
|
|
30042
|
+
// transcript so the web Console renders it verbatim; gated by console.watch (server → cli:
|
|
30043
|
+
// whether a web viewer is attached) so an unwatched cli syncs nothing.
|
|
30044
|
+
CONSOLE_SYNC: "console.sync",
|
|
30045
|
+
CONSOLE_WATCH: "console.watch",
|
|
29970
30046
|
// AI spec-assist (suggest/review/compose) moved off dedicated ai.* channels to
|
|
29971
30047
|
// command.dispatch({ ai:true }) → the profile-failover AI path (ADR-0100).
|
|
29972
30048
|
// quota & usage (ADR-0020)
|
|
@@ -30104,7 +30180,7 @@ var OutputBatcher = class {
|
|
|
30104
30180
|
};
|
|
30105
30181
|
|
|
30106
30182
|
// src/core/ws-client.ts
|
|
30107
|
-
import { existsSync as
|
|
30183
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, rmSync as rmSync4 } from "fs";
|
|
30108
30184
|
import { randomUUID as randomCommandId } from "crypto";
|
|
30109
30185
|
|
|
30110
30186
|
// src/core/update-scheduler.ts
|
|
@@ -30347,7 +30423,7 @@ async function runCommand(dispatch, emit) {
|
|
|
30347
30423
|
});
|
|
30348
30424
|
child.stdout?.on("data", (data) => batcher.push(data.toString("utf8")));
|
|
30349
30425
|
child.stderr?.on("data", (data) => batcher.push(data.toString("utf8")));
|
|
30350
|
-
await new Promise((
|
|
30426
|
+
await new Promise((resolve7) => {
|
|
30351
30427
|
child.on("close", (exitCode) => {
|
|
30352
30428
|
batcher.flush();
|
|
30353
30429
|
emit({
|
|
@@ -30357,7 +30433,7 @@ async function runCommand(dispatch, emit) {
|
|
|
30357
30433
|
done: true,
|
|
30358
30434
|
exitCode: exitCode ?? -1
|
|
30359
30435
|
});
|
|
30360
|
-
|
|
30436
|
+
resolve7();
|
|
30361
30437
|
});
|
|
30362
30438
|
child.on("error", (err) => {
|
|
30363
30439
|
emit({
|
|
@@ -30368,7 +30444,7 @@ async function runCommand(dispatch, emit) {
|
|
|
30368
30444
|
done: true,
|
|
30369
30445
|
exitCode: -1
|
|
30370
30446
|
});
|
|
30371
|
-
|
|
30447
|
+
resolve7();
|
|
30372
30448
|
});
|
|
30373
30449
|
});
|
|
30374
30450
|
}
|
|
@@ -30448,10 +30524,10 @@ function isSessionLimit(text) {
|
|
|
30448
30524
|
);
|
|
30449
30525
|
}
|
|
30450
30526
|
function runAttempt(commandId, cmd, args, cwd2, env3, onChunk) {
|
|
30451
|
-
return new Promise((
|
|
30527
|
+
return new Promise((resolve7) => {
|
|
30452
30528
|
void runCommand({ commandId, cmd, args, path: cwd2, env: env3 }, (out) => {
|
|
30453
30529
|
if (out.chunk) onChunk(out.chunk);
|
|
30454
|
-
if (out.done)
|
|
30530
|
+
if (out.done) resolve7(out.exitCode ?? -1);
|
|
30455
30531
|
});
|
|
30456
30532
|
});
|
|
30457
30533
|
}
|
|
@@ -30629,16 +30705,16 @@ async function fetchUsage(token) {
|
|
|
30629
30705
|
}
|
|
30630
30706
|
}
|
|
30631
30707
|
function runUsageCli(dir2, aiCli2) {
|
|
30632
|
-
return new Promise((
|
|
30708
|
+
return new Promise((resolve7) => {
|
|
30633
30709
|
let out = "";
|
|
30634
30710
|
let child = null;
|
|
30635
30711
|
const finish = () => {
|
|
30636
30712
|
clearTimeout(timer);
|
|
30637
|
-
|
|
30713
|
+
resolve7(out);
|
|
30638
30714
|
};
|
|
30639
30715
|
const timer = setTimeout(() => {
|
|
30640
30716
|
child?.kill();
|
|
30641
|
-
|
|
30717
|
+
resolve7(out);
|
|
30642
30718
|
}, 3e4);
|
|
30643
30719
|
timer.unref?.();
|
|
30644
30720
|
try {
|
|
@@ -31069,24 +31145,210 @@ async function readWorkerFile(path) {
|
|
|
31069
31145
|
}
|
|
31070
31146
|
}
|
|
31071
31147
|
|
|
31072
|
-
// src/core/
|
|
31073
|
-
import {
|
|
31074
|
-
import {
|
|
31075
|
-
|
|
31148
|
+
// src/core/fs-write.ts
|
|
31149
|
+
import { mkdir as mkdir2, writeFile } from "fs/promises";
|
|
31150
|
+
import { dirname as dirname3, resolve as resolve3, sep as sep3 } from "path";
|
|
31151
|
+
var MAX_BYTES2 = 2 * 1024 * 1024;
|
|
31152
|
+
function resolveInRoot(root, relPath) {
|
|
31153
|
+
if (!root) return null;
|
|
31154
|
+
const base = resolve3(root);
|
|
31155
|
+
const target = resolve3(base, relPath || ".");
|
|
31156
|
+
return target === base || target.startsWith(base + sep3) ? target : null;
|
|
31157
|
+
}
|
|
31158
|
+
async function writeWorkerFile(root, relPath, content) {
|
|
31159
|
+
const target = resolveInRoot(root, relPath);
|
|
31160
|
+
if (!target) {
|
|
31161
|
+
return { ok: false, path: relPath, bytes: 0, error: "path escapes the project root" };
|
|
31162
|
+
}
|
|
31163
|
+
const bytes = Buffer.byteLength(content, "utf8");
|
|
31164
|
+
if (bytes > MAX_BYTES2) {
|
|
31165
|
+
return { ok: false, path: target, bytes: 0, error: `content too large (max ${MAX_BYTES2} bytes)` };
|
|
31166
|
+
}
|
|
31167
|
+
try {
|
|
31168
|
+
await mkdir2(dirname3(target), { recursive: true });
|
|
31169
|
+
await writeFile(target, content, "utf8");
|
|
31170
|
+
return { ok: true, path: target, bytes };
|
|
31171
|
+
} catch (err) {
|
|
31172
|
+
return { ok: false, path: target, bytes: 0, error: err instanceof Error ? err.message : String(err) };
|
|
31173
|
+
}
|
|
31174
|
+
}
|
|
31175
|
+
|
|
31176
|
+
// src/core/autonomous.ts
|
|
31177
|
+
import { readFile as readFile2, writeFile as writeFile2, chmod } from "fs/promises";
|
|
31178
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
31179
|
+
import { execFile as execFile2, spawn as spawn4 } from "child_process";
|
|
31180
|
+
import { join as join12 } from "path";
|
|
31076
31181
|
import { promisify as promisify2 } from "util";
|
|
31077
31182
|
var run2 = promisify2(execFile2);
|
|
31183
|
+
var SETTINGS_REL = ".claude/.autonomous.settings.json";
|
|
31184
|
+
var HISTORIES_REL = ".claude/.autonomous.histories.json";
|
|
31185
|
+
var APPROVALS_REL = ".claude/.autonomous.approvals.json";
|
|
31186
|
+
var TICK_REL = ".claude/hooks/autonomous-tick.sh";
|
|
31187
|
+
var LOG_DIR_REL = ".claude/logs";
|
|
31188
|
+
var BOOK_FILES = {
|
|
31189
|
+
userTodo: "USER_TODO.md",
|
|
31190
|
+
aiTodo: "AI_TODO.md",
|
|
31191
|
+
aiProgress: "AI_PROGRESS.md",
|
|
31192
|
+
aiDone: "AI_DONE.md",
|
|
31193
|
+
userQa: "USER_QA.md"
|
|
31194
|
+
};
|
|
31195
|
+
var DEFAULT_CRON = "*/10 * * * *";
|
|
31196
|
+
async function readText(path, fallback = "") {
|
|
31197
|
+
try {
|
|
31198
|
+
return await readFile2(path, "utf8");
|
|
31199
|
+
} catch {
|
|
31200
|
+
return fallback;
|
|
31201
|
+
}
|
|
31202
|
+
}
|
|
31203
|
+
function parseJson(text) {
|
|
31204
|
+
try {
|
|
31205
|
+
const v = JSON.parse(text);
|
|
31206
|
+
return v && typeof v === "object" ? v : null;
|
|
31207
|
+
} catch {
|
|
31208
|
+
return null;
|
|
31209
|
+
}
|
|
31210
|
+
}
|
|
31211
|
+
function tickScript(root) {
|
|
31212
|
+
return join12(root, TICK_REL);
|
|
31213
|
+
}
|
|
31214
|
+
async function crontabList() {
|
|
31215
|
+
try {
|
|
31216
|
+
const { stdout } = await run2("crontab", ["-l"], { timeout: 1e4 });
|
|
31217
|
+
return stdout;
|
|
31218
|
+
} catch {
|
|
31219
|
+
return "";
|
|
31220
|
+
}
|
|
31221
|
+
}
|
|
31222
|
+
async function crontabSet(content) {
|
|
31223
|
+
return new Promise((resolve7) => {
|
|
31224
|
+
const child = spawn4("crontab", ["-"], { stdio: ["pipe", "ignore", "ignore"] });
|
|
31225
|
+
child.on("error", () => resolve7(false));
|
|
31226
|
+
child.on("close", (code) => resolve7(code === 0));
|
|
31227
|
+
child.stdin.end(content.endsWith("\n") ? content : content + "\n");
|
|
31228
|
+
});
|
|
31229
|
+
}
|
|
31230
|
+
async function cronInstalled(root) {
|
|
31231
|
+
const script = tickScript(root);
|
|
31232
|
+
return (await crontabList()).split("\n").some((l) => l.includes(script) && !l.trim().startsWith("#"));
|
|
31233
|
+
}
|
|
31234
|
+
async function getAutonomousStatus(root) {
|
|
31235
|
+
const settings = parseJson(await readText(join12(root, SETTINGS_REL))) ?? {};
|
|
31236
|
+
const hist = parseJson(await readText(join12(root, HISTORIES_REL))) ?? {};
|
|
31237
|
+
const records = Array.isArray(hist.records) ? hist.records : [];
|
|
31238
|
+
const last = records[records.length - 1];
|
|
31239
|
+
const ticks = hist.ticks ?? {};
|
|
31240
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
31241
|
+
return {
|
|
31242
|
+
installed: await cronInstalled(root),
|
|
31243
|
+
paused: settings.paused === true,
|
|
31244
|
+
cronSchedule: typeof settings.cron_schedule === "string" ? settings.cron_schedule : DEFAULT_CRON,
|
|
31245
|
+
lastTickAt: last && typeof last.ts === "string" ? last.ts : null,
|
|
31246
|
+
lastResult: last && typeof last.status === "string" ? last.status : null,
|
|
31247
|
+
consecutiveFails: typeof hist.consecutive_fails === "number" ? hist.consecutive_fails : 0,
|
|
31248
|
+
todayTicks: ticks.day === today && typeof ticks.count === "number" ? ticks.count : 0
|
|
31249
|
+
};
|
|
31250
|
+
}
|
|
31251
|
+
function isAutonomousRunning(root) {
|
|
31252
|
+
try {
|
|
31253
|
+
const s = JSON.parse(readFileSync12(join12(root, SETTINGS_REL), "utf8"));
|
|
31254
|
+
if (s.paused === true) return false;
|
|
31255
|
+
const h = JSON.parse(readFileSync12(join12(root, HISTORIES_REL), "utf8"));
|
|
31256
|
+
const ts = h.records?.[h.records.length - 1]?.ts;
|
|
31257
|
+
if (!ts) return false;
|
|
31258
|
+
const t = Date.parse(ts.replace(" ", "T"));
|
|
31259
|
+
return Number.isFinite(t) && Date.now() - t < 6 * 3600 * 1e3;
|
|
31260
|
+
} catch {
|
|
31261
|
+
return false;
|
|
31262
|
+
}
|
|
31263
|
+
}
|
|
31264
|
+
async function readAutonomous(root) {
|
|
31265
|
+
const [settings, approvals, status, ...books] = await Promise.all([
|
|
31266
|
+
readText(join12(root, SETTINGS_REL)),
|
|
31267
|
+
readText(join12(root, APPROVALS_REL), "{}"),
|
|
31268
|
+
getAutonomousStatus(root),
|
|
31269
|
+
...Object.values(BOOK_FILES).map((f) => readText(join12(root, f)))
|
|
31270
|
+
]);
|
|
31271
|
+
const keys = Object.keys(BOOK_FILES);
|
|
31272
|
+
const bookMap = {};
|
|
31273
|
+
keys.forEach((k, i) => bookMap[k] = books[i] ?? "");
|
|
31274
|
+
return { settings, status, books: bookMap, approvals };
|
|
31275
|
+
}
|
|
31276
|
+
async function readAutonomousLogs(root, date) {
|
|
31277
|
+
const day = date && /^\d{4}-\d{2}-\d{2}$/.test(date) ? date : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
31278
|
+
const text = await readText(join12(root, LOG_DIR_REL, `autonomous-tick-${day}.log`));
|
|
31279
|
+
const lines = text ? text.split("\n").filter((l) => l.length > 0).slice(-500) : [];
|
|
31280
|
+
return { date: day, lines };
|
|
31281
|
+
}
|
|
31282
|
+
async function installCron(root) {
|
|
31283
|
+
const script = tickScript(root);
|
|
31284
|
+
const status = await getAutonomousStatus(root);
|
|
31285
|
+
await chmod(script, 493).catch(() => void 0);
|
|
31286
|
+
const kept = (await crontabList()).split("\n").filter((l) => l.trim() && !l.includes(script));
|
|
31287
|
+
kept.push(`${status.cronSchedule} ${script}`);
|
|
31288
|
+
await crontabSet(kept.join("\n"));
|
|
31289
|
+
}
|
|
31290
|
+
async function uninstallCron(root) {
|
|
31291
|
+
const script = tickScript(root);
|
|
31292
|
+
const cur = await crontabList();
|
|
31293
|
+
if (!cur.includes(script)) return;
|
|
31294
|
+
const kept = cur.split("\n").filter((l) => l.trim() && !l.includes(script));
|
|
31295
|
+
await crontabSet(kept.join("\n"));
|
|
31296
|
+
}
|
|
31297
|
+
async function writeAutonomous(root, req, by) {
|
|
31298
|
+
try {
|
|
31299
|
+
switch (req.kind) {
|
|
31300
|
+
case "settings": {
|
|
31301
|
+
if (!parseJson(req.settings)) return { ok: false, error: "settings is not valid JSON" };
|
|
31302
|
+
await writeFile2(join12(root, SETTINGS_REL), req.settings, "utf8");
|
|
31303
|
+
break;
|
|
31304
|
+
}
|
|
31305
|
+
case "approvals": {
|
|
31306
|
+
const map = parseJson(await readText(join12(root, APPROVALS_REL), "{}")) ?? {};
|
|
31307
|
+
if (req.approved) map[req.taskId] = { approved: true, by, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
31308
|
+
else delete map[req.taskId];
|
|
31309
|
+
await writeFile2(join12(root, APPROVALS_REL), JSON.stringify(map, null, 2) + "\n", "utf8");
|
|
31310
|
+
break;
|
|
31311
|
+
}
|
|
31312
|
+
case "userTodo": {
|
|
31313
|
+
const cur = await readText(join12(root, BOOK_FILES.userTodo));
|
|
31314
|
+
const block = `
|
|
31315
|
+
|
|
31316
|
+
> posted by ${by} at ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
31317
|
+
${req.content.trim()}
|
|
31318
|
+
`;
|
|
31319
|
+
await writeFile2(join12(root, BOOK_FILES.userTodo), cur.trimEnd() + block, "utf8");
|
|
31320
|
+
break;
|
|
31321
|
+
}
|
|
31322
|
+
case "cron": {
|
|
31323
|
+
if (req.action === "install") await installCron(root);
|
|
31324
|
+
else await uninstallCron(root);
|
|
31325
|
+
break;
|
|
31326
|
+
}
|
|
31327
|
+
}
|
|
31328
|
+
return { ok: true, status: await getAutonomousStatus(root) };
|
|
31329
|
+
} catch (err) {
|
|
31330
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
31331
|
+
}
|
|
31332
|
+
}
|
|
31333
|
+
|
|
31334
|
+
// src/core/git-diff.ts
|
|
31335
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
31336
|
+
import { execFile as execFile3 } from "child_process";
|
|
31337
|
+
import { basename as basename2, dirname as dirname4, resolve as resolve4 } from "path";
|
|
31338
|
+
import { promisify as promisify3 } from "util";
|
|
31339
|
+
var run3 = promisify3(execFile3);
|
|
31078
31340
|
async function gitDiff(path) {
|
|
31079
|
-
const target =
|
|
31080
|
-
const dir2 =
|
|
31341
|
+
const target = resolve4(path);
|
|
31342
|
+
const dir2 = dirname4(target);
|
|
31081
31343
|
const base = basename2(target);
|
|
31082
31344
|
let newContent = "";
|
|
31083
31345
|
try {
|
|
31084
|
-
newContent = await
|
|
31346
|
+
newContent = await readFile3(target, "utf8");
|
|
31085
31347
|
} catch {
|
|
31086
31348
|
}
|
|
31087
31349
|
let oldContent = "";
|
|
31088
31350
|
try {
|
|
31089
|
-
const { stdout } = await
|
|
31351
|
+
const { stdout } = await run3("git", ["show", `HEAD:./${base}`], {
|
|
31090
31352
|
cwd: dir2,
|
|
31091
31353
|
timeout: 15e3,
|
|
31092
31354
|
maxBuffer: 4 * 1024 * 1024
|
|
@@ -31098,12 +31360,12 @@ async function gitDiff(path) {
|
|
|
31098
31360
|
}
|
|
31099
31361
|
|
|
31100
31362
|
// src/core/git-env.ts
|
|
31101
|
-
import { execFile as
|
|
31102
|
-
import { promisify as
|
|
31103
|
-
var
|
|
31363
|
+
import { execFile as execFile4 } from "child_process";
|
|
31364
|
+
import { promisify as promisify4 } from "util";
|
|
31365
|
+
var run4 = promisify4(execFile4);
|
|
31104
31366
|
async function hasBinary(bin, args = ["--version"]) {
|
|
31105
31367
|
try {
|
|
31106
|
-
await
|
|
31368
|
+
await run4(bin, args, { timeout: 5e3 });
|
|
31107
31369
|
return true;
|
|
31108
31370
|
} catch {
|
|
31109
31371
|
return false;
|
|
@@ -31116,7 +31378,7 @@ async function checkGitEnv(provider) {
|
|
|
31116
31378
|
let account = null;
|
|
31117
31379
|
if (installed) {
|
|
31118
31380
|
try {
|
|
31119
|
-
const { stdout, stderr } = await
|
|
31381
|
+
const { stdout, stderr } = await run4(bin, ["auth", "status"], { timeout: 8e3 });
|
|
31120
31382
|
const out = `${stdout}
|
|
31121
31383
|
${stderr}`;
|
|
31122
31384
|
authenticated = /logged in|logged into|✓/i.test(out);
|
|
@@ -31130,21 +31392,21 @@ ${stderr}`;
|
|
|
31130
31392
|
|
|
31131
31393
|
// src/core/git-history.ts
|
|
31132
31394
|
import { readdir as readdir3 } from "fs/promises";
|
|
31133
|
-
import { execFile as
|
|
31134
|
-
import { resolve as
|
|
31135
|
-
import { promisify as
|
|
31136
|
-
var
|
|
31395
|
+
import { execFile as execFile5 } from "child_process";
|
|
31396
|
+
import { resolve as resolve5, sep as sep4 } from "path";
|
|
31397
|
+
import { promisify as promisify5 } from "util";
|
|
31398
|
+
var run5 = promisify5(execFile5);
|
|
31137
31399
|
var FS = "";
|
|
31138
31400
|
var RS = "";
|
|
31139
31401
|
function resolveRepo(root, repo) {
|
|
31140
31402
|
if (!root) return null;
|
|
31141
|
-
const base =
|
|
31142
|
-
const requested =
|
|
31143
|
-
return requested === base || requested.startsWith(base +
|
|
31403
|
+
const base = resolve5(root);
|
|
31404
|
+
const requested = resolve5(base, repo && repo.trim() ? repo : ".");
|
|
31405
|
+
return requested === base || requested.startsWith(base + sep4) ? requested : base;
|
|
31144
31406
|
}
|
|
31145
31407
|
async function git(cwd2, args) {
|
|
31146
31408
|
try {
|
|
31147
|
-
const { stdout } = await
|
|
31409
|
+
const { stdout } = await run5("git", args, {
|
|
31148
31410
|
cwd: cwd2,
|
|
31149
31411
|
timeout: 15e3,
|
|
31150
31412
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -31156,13 +31418,13 @@ async function git(cwd2, args) {
|
|
|
31156
31418
|
}
|
|
31157
31419
|
async function gitRepos(root) {
|
|
31158
31420
|
if (!root) return { repos: [] };
|
|
31159
|
-
const base =
|
|
31421
|
+
const base = resolve5(root);
|
|
31160
31422
|
const found = /* @__PURE__ */ new Set();
|
|
31161
31423
|
const repos = [];
|
|
31162
31424
|
const add = async (dir2) => {
|
|
31163
31425
|
const inside = await git(dir2, ["rev-parse", "--show-toplevel"]);
|
|
31164
31426
|
const top = inside.trim();
|
|
31165
|
-
if (!top || found.has(top) || !(top === base || top.startsWith(base +
|
|
31427
|
+
if (!top || found.has(top) || !(top === base || top.startsWith(base + sep4))) return;
|
|
31166
31428
|
found.add(top);
|
|
31167
31429
|
const subdir = top === base ? "" : top.slice(base.length + 1);
|
|
31168
31430
|
const remote = (await git(top, ["remote", "get-url", "origin"])).trim();
|
|
@@ -31173,13 +31435,13 @@ async function gitRepos(root) {
|
|
|
31173
31435
|
const lvl1 = await readdir3(base, { withFileTypes: true });
|
|
31174
31436
|
for (const d of lvl1) {
|
|
31175
31437
|
if (!d.isDirectory() || d.name === ".git" || d.name === "node_modules") continue;
|
|
31176
|
-
const p1 =
|
|
31438
|
+
const p1 = resolve5(base, d.name);
|
|
31177
31439
|
await add(p1);
|
|
31178
31440
|
try {
|
|
31179
31441
|
const lvl2 = await readdir3(p1, { withFileTypes: true });
|
|
31180
31442
|
for (const d2 of lvl2) {
|
|
31181
31443
|
if (!d2.isDirectory() || d2.name === ".git" || d2.name === "node_modules") continue;
|
|
31182
|
-
await add(
|
|
31444
|
+
await add(resolve5(p1, d2.name));
|
|
31183
31445
|
}
|
|
31184
31446
|
} catch {
|
|
31185
31447
|
}
|
|
@@ -31246,24 +31508,24 @@ async function gitCommitDiff(root, repo, hash, path) {
|
|
|
31246
31508
|
}
|
|
31247
31509
|
|
|
31248
31510
|
// src/core/git-commit-identity.ts
|
|
31249
|
-
import { execFile as
|
|
31250
|
-
import { promisify as
|
|
31251
|
-
var
|
|
31511
|
+
import { execFile as execFile6 } from "child_process";
|
|
31512
|
+
import { promisify as promisify6 } from "util";
|
|
31513
|
+
var run6 = promisify6(execFile6);
|
|
31252
31514
|
async function setCommitAuthor(cwd2, machineUsername) {
|
|
31253
31515
|
if (!machineUsername) return;
|
|
31254
31516
|
try {
|
|
31255
|
-
await
|
|
31517
|
+
await run6("git", ["config", "--local", "user.name", machineUsername], { cwd: cwd2, timeout: 8e3 });
|
|
31256
31518
|
} catch {
|
|
31257
31519
|
}
|
|
31258
31520
|
}
|
|
31259
31521
|
|
|
31260
31522
|
// src/core/scaffold.ts
|
|
31261
|
-
import { cp, mkdir as
|
|
31262
|
-
import { execFile as
|
|
31263
|
-
import { dirname as
|
|
31523
|
+
import { cp, mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
31524
|
+
import { execFile as execFile7 } from "child_process";
|
|
31525
|
+
import { dirname as dirname5, join as join13, resolve as resolve6 } from "path";
|
|
31264
31526
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
31265
|
-
import { promisify as
|
|
31266
|
-
var
|
|
31527
|
+
import { promisify as promisify7 } from "util";
|
|
31528
|
+
var run7 = promisify7(execFile7);
|
|
31267
31529
|
function reposOf(spec) {
|
|
31268
31530
|
const repos = spec?.repos;
|
|
31269
31531
|
return Array.isArray(repos) ? repos : [];
|
|
@@ -31277,46 +31539,46 @@ async function provisionRepos(target, repos, emit, opts = {}) {
|
|
|
31277
31539
|
if (repo.primary) {
|
|
31278
31540
|
if (opts.clonePrimary && repo.op === "existing" && repo.url) {
|
|
31279
31541
|
emit("git", `Cloning ${repo.url}\u2026`);
|
|
31280
|
-
await
|
|
31542
|
+
await run7("git", ["clone", repo.url, target], { timeout: 12e4 });
|
|
31281
31543
|
continue;
|
|
31282
31544
|
}
|
|
31283
31545
|
emit("git", "Initializing the primary repo\u2026");
|
|
31284
|
-
await
|
|
31546
|
+
await run7("git", ["init"], { cwd: target, timeout: 2e4 });
|
|
31285
31547
|
if (repo.op === "existing" && repo.url) {
|
|
31286
|
-
await
|
|
31548
|
+
await run7("git", ["remote", "add", "origin", repo.url], { cwd: target, timeout: 2e4 }).catch(
|
|
31287
31549
|
() => void 0
|
|
31288
31550
|
);
|
|
31289
31551
|
}
|
|
31290
31552
|
continue;
|
|
31291
31553
|
}
|
|
31292
|
-
const dir2 =
|
|
31554
|
+
const dir2 = join13(target, subDirName(repo));
|
|
31293
31555
|
if (repo.op === "existing" && repo.url) {
|
|
31294
31556
|
emit("git", `Cloning sub-repo ${repo.url}\u2026`);
|
|
31295
|
-
await
|
|
31557
|
+
await run7("git", ["clone", repo.url, dir2], { timeout: 12e4 });
|
|
31296
31558
|
} else {
|
|
31297
31559
|
emit("git", `Initializing sub-repo ${subDirName(repo)}\u2026`);
|
|
31298
|
-
await
|
|
31299
|
-
await
|
|
31560
|
+
await mkdir3(dir2, { recursive: true });
|
|
31561
|
+
await run7("git", ["init"], { cwd: dir2, timeout: 2e4 });
|
|
31300
31562
|
}
|
|
31301
31563
|
}
|
|
31302
31564
|
}
|
|
31303
31565
|
function sampleDir() {
|
|
31304
31566
|
if (process.env.SCAFFOLD_SAMPLE_DIR) return process.env.SCAFFOLD_SAMPLE_DIR;
|
|
31305
|
-
const here =
|
|
31306
|
-
return
|
|
31567
|
+
const here = dirname5(fileURLToPath3(import.meta.url));
|
|
31568
|
+
return resolve6(here, "../../project-sample");
|
|
31307
31569
|
}
|
|
31308
31570
|
async function scaffoldProject(payload, profileDir2, onProgress) {
|
|
31309
31571
|
const emit = (step, message) => onProgress?.({ projectId: payload.projectId, step, message });
|
|
31310
31572
|
try {
|
|
31311
|
-
const root = process.env.SCAFFOLD_ROOT ?
|
|
31312
|
-
const target =
|
|
31313
|
-
await
|
|
31573
|
+
const root = process.env.SCAFFOLD_ROOT ? resolve6(process.env.SCAFFOLD_ROOT) : resolve6(profileDir2);
|
|
31574
|
+
const target = join13(root, payload.projectName);
|
|
31575
|
+
await mkdir3(target, { recursive: true });
|
|
31314
31576
|
emit("copy", "Copying the sample template\u2026");
|
|
31315
31577
|
await cp(sampleDir(), target, { recursive: true, force: false, errorOnExist: false });
|
|
31316
31578
|
if (payload.spec) {
|
|
31317
31579
|
emit("spec", "Writing project.spec.json\u2026");
|
|
31318
|
-
await
|
|
31319
|
-
|
|
31580
|
+
await writeFile3(
|
|
31581
|
+
join13(target, "project.spec.json"),
|
|
31320
31582
|
JSON.stringify(payload.spec, null, 2),
|
|
31321
31583
|
"utf8"
|
|
31322
31584
|
);
|
|
@@ -31334,7 +31596,7 @@ async function aiInit(target, spec, emit) {
|
|
|
31334
31596
|
emit("ai-init", "AI init: subagents, README, CLAUDE.md\u2026");
|
|
31335
31597
|
const subagents = Array.isArray(spec.subagents) ? spec.subagents : [];
|
|
31336
31598
|
if (subagents.length > 0) {
|
|
31337
|
-
await
|
|
31599
|
+
await mkdir3(join13(target, ".claude", "agents"), { recursive: true });
|
|
31338
31600
|
for (const sa of subagents) {
|
|
31339
31601
|
const name = (sa.name || "").trim().replace(/[^\w.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
31340
31602
|
if (!name) continue;
|
|
@@ -31344,17 +31606,17 @@ name: ${name}
|
|
|
31344
31606
|
|
|
31345
31607
|
${(sa.description || "").trim()}
|
|
31346
31608
|
`;
|
|
31347
|
-
await
|
|
31609
|
+
await writeFile3(join13(target, ".claude", "agents", `${name}.md`), body, "utf8");
|
|
31348
31610
|
}
|
|
31349
31611
|
}
|
|
31350
31612
|
const specJson = JSON.stringify(spec);
|
|
31351
31613
|
await generateFile(
|
|
31352
|
-
|
|
31614
|
+
join13(target, "README.md"),
|
|
31353
31615
|
`Write a concise README.md (Markdown only, no preamble) for this project from its spec JSON:
|
|
31354
31616
|
${specJson}`
|
|
31355
31617
|
);
|
|
31356
31618
|
await generateFile(
|
|
31357
|
-
|
|
31619
|
+
join13(target, "CLAUDE.md"),
|
|
31358
31620
|
`Write a CLAUDE.md (Markdown only, no preamble) with guidance/conventions for AI agents working in this project, derived from its spec JSON:
|
|
31359
31621
|
${specJson}`
|
|
31360
31622
|
);
|
|
@@ -31362,16 +31624,16 @@ ${specJson}`
|
|
|
31362
31624
|
async function generateFile(path, prompt) {
|
|
31363
31625
|
try {
|
|
31364
31626
|
const text = await aiGenerate(prompt);
|
|
31365
|
-
if (text.trim()) await
|
|
31627
|
+
if (text.trim()) await writeFile3(path, text.trim() + "\n", "utf8");
|
|
31366
31628
|
} catch {
|
|
31367
31629
|
}
|
|
31368
31630
|
}
|
|
31369
31631
|
async function addProject(payload, profileDir2, onProgress) {
|
|
31370
31632
|
const emit = (step, message) => onProgress?.({ projectId: payload.projectId, step, message });
|
|
31371
31633
|
try {
|
|
31372
|
-
const root = process.env.SCAFFOLD_ROOT ?
|
|
31373
|
-
const target =
|
|
31374
|
-
await
|
|
31634
|
+
const root = process.env.SCAFFOLD_ROOT ? resolve6(process.env.SCAFFOLD_ROOT) : resolve6(profileDir2);
|
|
31635
|
+
const target = join13(root, payload.projectName);
|
|
31636
|
+
await mkdir3(target, { recursive: true });
|
|
31375
31637
|
const repos = payload.repos ?? [];
|
|
31376
31638
|
if (repos.length > 0) await provisionRepos(target, repos, emit, { clonePrimary: true });
|
|
31377
31639
|
onProgress?.({ projectId: payload.projectId, step: "done", message: "Project added.", done: true });
|
|
@@ -31381,6 +31643,40 @@ async function addProject(payload, profileDir2, onProgress) {
|
|
|
31381
31643
|
}
|
|
31382
31644
|
}
|
|
31383
31645
|
|
|
31646
|
+
// src/core/config-sync.ts
|
|
31647
|
+
import { existsSync as existsSync7, readFileSync as readFileSync13 } from "fs";
|
|
31648
|
+
import { join as join14 } from "path";
|
|
31649
|
+
var SERVER_MANAGED_KEYS = ["physicPath", "sessionSwitchPct", "perPromptTokenLimit"];
|
|
31650
|
+
function readConfigText(profileDir2) {
|
|
31651
|
+
const path = join14(profileDir2, "config.json");
|
|
31652
|
+
if (existsSync7(path)) {
|
|
31653
|
+
try {
|
|
31654
|
+
return readFileSync13(path, "utf8");
|
|
31655
|
+
} catch {
|
|
31656
|
+
}
|
|
31657
|
+
}
|
|
31658
|
+
return JSON.stringify(defaultProfileConfig(), null, 2);
|
|
31659
|
+
}
|
|
31660
|
+
function applyConfigText(profileDir2, text) {
|
|
31661
|
+
let parsed;
|
|
31662
|
+
try {
|
|
31663
|
+
parsed = JSON.parse(text);
|
|
31664
|
+
} catch (err) {
|
|
31665
|
+
return { ok: false, error: `Invalid JSON: ${err.message}` };
|
|
31666
|
+
}
|
|
31667
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
31668
|
+
return { ok: false, error: "config.json must be a JSON object." };
|
|
31669
|
+
}
|
|
31670
|
+
const next = { ...parsed };
|
|
31671
|
+
const current = readProfileConfig(profileDir2);
|
|
31672
|
+
for (const key of SERVER_MANAGED_KEYS) {
|
|
31673
|
+
if (current[key] !== void 0) next[key] = current[key];
|
|
31674
|
+
else delete next[key];
|
|
31675
|
+
}
|
|
31676
|
+
overwriteProfileConfig(profileDir2, next);
|
|
31677
|
+
return { ok: true };
|
|
31678
|
+
}
|
|
31679
|
+
|
|
31384
31680
|
// src/core/ws-client.ts
|
|
31385
31681
|
var HEARTBEAT_MS = 3e4;
|
|
31386
31682
|
var USAGE_POLL_MS = 5 * 6e4;
|
|
@@ -31397,6 +31693,11 @@ function envReconnectMaxSec() {
|
|
|
31397
31693
|
var MAX_HASHCODE_INVALID_RETRIES = 5;
|
|
31398
31694
|
var CLOSE_DRAINING = 4001;
|
|
31399
31695
|
var OFFLINE_BUFFER_MAX_BYTES = 512 * 1024;
|
|
31696
|
+
var CONSOLE_SNAPSHOT_INTERVAL_MS = 3e4;
|
|
31697
|
+
var CONSOLE_WATCH_LEASE_MS = 45e3;
|
|
31698
|
+
function toDtoEntry(e) {
|
|
31699
|
+
return { id: e.id, source: e.source, kind: e.kind, text: e.text, level: e.level, resultKind: e.resultKind };
|
|
31700
|
+
}
|
|
31400
31701
|
var WsClient = class {
|
|
31401
31702
|
constructor(context) {
|
|
31402
31703
|
this.context = context;
|
|
@@ -31408,6 +31709,11 @@ var WsClient = class {
|
|
|
31408
31709
|
);
|
|
31409
31710
|
context.bus.onLocalSubmit((input) => void this.runLocalCommand(input));
|
|
31410
31711
|
context.bus.onReconnect(() => this.reconnectNow());
|
|
31712
|
+
context.bus.onTranscript((entry) => this.emitConsole({ kind: "add", entry: toDtoEntry(entry) }));
|
|
31713
|
+
context.bus.onTranscriptUpdate(
|
|
31714
|
+
(entry) => this.emitConsole({ kind: "update", entry: toDtoEntry(entry) })
|
|
31715
|
+
);
|
|
31716
|
+
context.bus.onClear(() => this.emitConsole({ kind: "clear" }));
|
|
31411
31717
|
}
|
|
31412
31718
|
context;
|
|
31413
31719
|
socket = null;
|
|
@@ -31454,6 +31760,18 @@ var WsClient = class {
|
|
|
31454
31760
|
*/
|
|
31455
31761
|
handledCommands = /* @__PURE__ */ new Set();
|
|
31456
31762
|
offlineBufferBytes = 0;
|
|
31763
|
+
/**
|
|
31764
|
+
* Console-sync state (ADR-0150). `consoleWatching` is set by `console.watch` from the server
|
|
31765
|
+
* (a web Console viewer is attached); only then does the cli emit `console.sync`. `consoleRev`
|
|
31766
|
+
* is the monotonic revision the web uses to detect a gap; `consoleSnapshotTimer` periodically
|
|
31767
|
+
* re-sends a full snapshot to self-heal. Watching resets to false on each reconnect (the server
|
|
31768
|
+
* re-asserts it), so a fresh session re-snapshots.
|
|
31769
|
+
*/
|
|
31770
|
+
consoleWatching = false;
|
|
31771
|
+
consoleRev = 0;
|
|
31772
|
+
consoleSnapshotTimer = null;
|
|
31773
|
+
/** Lease expiry: stop syncing if the server does not renew `console.watch` in time. */
|
|
31774
|
+
consoleWatchExpiry = null;
|
|
31457
31775
|
/** cli → server requests awaiting a reply (quota.check — ADR-0020). */
|
|
31458
31776
|
pending = /* @__PURE__ */ new Map();
|
|
31459
31777
|
/** Daily scheduled cli auto-update (ADR-0074); policy fed from each ws_token. */
|
|
@@ -31487,14 +31805,14 @@ var WsClient = class {
|
|
|
31487
31805
|
*/
|
|
31488
31806
|
awaitConnected(timeoutMs) {
|
|
31489
31807
|
if (this.sessionKey && this.bus.status === "connected") return Promise.resolve(true);
|
|
31490
|
-
return new Promise((
|
|
31808
|
+
return new Promise((resolve7) => {
|
|
31491
31809
|
const waiter = () => {
|
|
31492
31810
|
clearTimeout(timer);
|
|
31493
|
-
|
|
31811
|
+
resolve7(true);
|
|
31494
31812
|
};
|
|
31495
31813
|
const timer = setTimeout(() => {
|
|
31496
31814
|
this.connectWaiters = this.connectWaiters.filter((w) => w !== waiter);
|
|
31497
|
-
|
|
31815
|
+
resolve7(false);
|
|
31498
31816
|
}, timeoutMs);
|
|
31499
31817
|
this.connectWaiters.push(waiter);
|
|
31500
31818
|
});
|
|
@@ -31521,16 +31839,16 @@ var WsClient = class {
|
|
|
31521
31839
|
this.forceReconnect = false;
|
|
31522
31840
|
return Promise.resolve();
|
|
31523
31841
|
}
|
|
31524
|
-
return new Promise((
|
|
31842
|
+
return new Promise((resolve7) => {
|
|
31525
31843
|
const timer = setTimeout(() => {
|
|
31526
31844
|
this.wakeReconnect = null;
|
|
31527
|
-
|
|
31845
|
+
resolve7();
|
|
31528
31846
|
}, ms);
|
|
31529
31847
|
this.wakeReconnect = () => {
|
|
31530
31848
|
clearTimeout(timer);
|
|
31531
31849
|
this.wakeReconnect = null;
|
|
31532
31850
|
this.forceReconnect = false;
|
|
31533
|
-
|
|
31851
|
+
resolve7();
|
|
31534
31852
|
};
|
|
31535
31853
|
});
|
|
31536
31854
|
}
|
|
@@ -31586,7 +31904,7 @@ var WsClient = class {
|
|
|
31586
31904
|
* One WS session: token → connect → handshake → handle messages until close.
|
|
31587
31905
|
*/
|
|
31588
31906
|
connectOnce() {
|
|
31589
|
-
return new Promise((
|
|
31907
|
+
return new Promise((resolve7, reject) => {
|
|
31590
31908
|
void (async () => {
|
|
31591
31909
|
this.sessionEstablished = false;
|
|
31592
31910
|
this.bus.setStatus("connecting");
|
|
@@ -31655,7 +31973,7 @@ var WsClient = class {
|
|
|
31655
31973
|
if (code === CLOSE_DRAINING) {
|
|
31656
31974
|
this.bus.log("Server draining \u2014 will reconnect (backoff + jitter).");
|
|
31657
31975
|
}
|
|
31658
|
-
|
|
31976
|
+
resolve7();
|
|
31659
31977
|
});
|
|
31660
31978
|
socket.on("error", (err) => {
|
|
31661
31979
|
this.clearHeartbeat();
|
|
@@ -31682,6 +32000,7 @@ var WsClient = class {
|
|
|
31682
32000
|
this.physicRoot = null;
|
|
31683
32001
|
}
|
|
31684
32002
|
logger.info("ws.connected", { version: CLI_VERSION });
|
|
32003
|
+
this.stopConsoleSync();
|
|
31685
32004
|
this.bus.setStatus("connected");
|
|
31686
32005
|
this.bus.log("\u2714 Connected to the server (session encryption ready).");
|
|
31687
32006
|
this.sendMachineStatus();
|
|
@@ -31746,8 +32065,12 @@ var WsClient = class {
|
|
|
31746
32065
|
switch (message.channel) {
|
|
31747
32066
|
case WsChannels.PHYSIC_SYNC: {
|
|
31748
32067
|
const sync = payload;
|
|
31749
|
-
if (sync.oldName)
|
|
31750
|
-
|
|
32068
|
+
if (sync.oldName) {
|
|
32069
|
+
const oldRoot = this.physicFolderPath(sync.oldName);
|
|
32070
|
+
if (oldRoot) void uninstallCron(oldRoot).catch(() => void 0);
|
|
32071
|
+
}
|
|
32072
|
+
if (sync.oldName) rmSync4(join15(this.context.profileDir, sync.oldName), { recursive: true, force: true });
|
|
32073
|
+
mkdirSync4(join15(this.context.profileDir, sync.newName), { recursive: true });
|
|
31751
32074
|
this.physicRoot = this.physicFolderPath(sync.newName);
|
|
31752
32075
|
this.bus.setProject(sync.newName);
|
|
31753
32076
|
this.bus.log(`Physic project folder synced \u2192 ${sync.newName}`);
|
|
@@ -31756,7 +32079,9 @@ var WsClient = class {
|
|
|
31756
32079
|
case WsChannels.PHYSIC_DELETE: {
|
|
31757
32080
|
const del = payload;
|
|
31758
32081
|
if (del.name) {
|
|
31759
|
-
|
|
32082
|
+
const delRoot = this.physicFolderPath(del.name);
|
|
32083
|
+
if (delRoot) void uninstallCron(delRoot).catch(() => void 0);
|
|
32084
|
+
rmSync4(join15(this.context.profileDir, del.name), { recursive: true, force: true });
|
|
31760
32085
|
this.bus.log(`Physic project folder deleted (project removed) \u2192 ${del.name}`);
|
|
31761
32086
|
}
|
|
31762
32087
|
this.bus.setProject(null);
|
|
@@ -31821,6 +32146,53 @@ var WsClient = class {
|
|
|
31821
32146
|
(reply) => this.send(WsChannels.FS_READ, reply, message.id)
|
|
31822
32147
|
);
|
|
31823
32148
|
break;
|
|
32149
|
+
case WsChannels.FS_WRITE: {
|
|
32150
|
+
const req = payload;
|
|
32151
|
+
void writeWorkerFile(this.physicRoot, req.path, req.content).then(
|
|
32152
|
+
(reply) => this.send(WsChannels.FS_WRITE, reply, message.id)
|
|
32153
|
+
);
|
|
32154
|
+
break;
|
|
32155
|
+
}
|
|
32156
|
+
case WsChannels.AUTONOMOUS_READ:
|
|
32157
|
+
if (this.physicRoot) {
|
|
32158
|
+
void readAutonomous(this.physicRoot).then(
|
|
32159
|
+
(reply) => this.send(WsChannels.AUTONOMOUS_READ, reply, message.id)
|
|
32160
|
+
);
|
|
32161
|
+
}
|
|
32162
|
+
break;
|
|
32163
|
+
case WsChannels.AUTONOMOUS_LOGS: {
|
|
32164
|
+
const req = payload;
|
|
32165
|
+
if (this.physicRoot) {
|
|
32166
|
+
void readAutonomousLogs(this.physicRoot, req.date).then(
|
|
32167
|
+
(reply) => this.send(WsChannels.AUTONOMOUS_LOGS, reply, message.id)
|
|
32168
|
+
);
|
|
32169
|
+
}
|
|
32170
|
+
break;
|
|
32171
|
+
}
|
|
32172
|
+
case WsChannels.AUTONOMOUS_WRITE: {
|
|
32173
|
+
const req = payload;
|
|
32174
|
+
if (this.physicRoot) {
|
|
32175
|
+
void writeAutonomous(this.physicRoot, req, req.by ?? "unknown").then(
|
|
32176
|
+
(reply) => this.send(WsChannels.AUTONOMOUS_WRITE, reply, message.id)
|
|
32177
|
+
);
|
|
32178
|
+
}
|
|
32179
|
+
break;
|
|
32180
|
+
}
|
|
32181
|
+
case WsChannels.CONFIG_READ:
|
|
32182
|
+
this.send(
|
|
32183
|
+
WsChannels.CONFIG_READ,
|
|
32184
|
+
{ config: readConfigText(this.context.profileDir) },
|
|
32185
|
+
message.id
|
|
32186
|
+
);
|
|
32187
|
+
break;
|
|
32188
|
+
case WsChannels.CONFIG_WRITE: {
|
|
32189
|
+
const res = applyConfigText(
|
|
32190
|
+
this.context.profileDir,
|
|
32191
|
+
payload.config
|
|
32192
|
+
);
|
|
32193
|
+
this.send(WsChannels.CONFIG_WRITE, res, message.id);
|
|
32194
|
+
break;
|
|
32195
|
+
}
|
|
31824
32196
|
case WsChannels.GIT_DIFF:
|
|
31825
32197
|
void gitDiff(payload.path).then(
|
|
31826
32198
|
(reply) => this.send(WsChannels.GIT_DIFF, reply, message.id)
|
|
@@ -31859,7 +32231,7 @@ var WsClient = class {
|
|
|
31859
32231
|
}
|
|
31860
32232
|
case WsChannels.LOG_READ: {
|
|
31861
32233
|
const limit = Math.min(Math.max(payload.limit ?? 200, 1), 2e3);
|
|
31862
|
-
const lines = readRecentLogLines(
|
|
32234
|
+
const lines = readRecentLogLines(join15(this.context.profileDir, "logs"), limit);
|
|
31863
32235
|
this.send(WsChannels.LOG_READ, { lines }, message.id);
|
|
31864
32236
|
break;
|
|
31865
32237
|
}
|
|
@@ -31889,6 +32261,9 @@ var WsClient = class {
|
|
|
31889
32261
|
(reply) => this.send(WsChannels.REVIEW_EVALUATE, reply, message.id)
|
|
31890
32262
|
);
|
|
31891
32263
|
break;
|
|
32264
|
+
case WsChannels.CONSOLE_WATCH:
|
|
32265
|
+
this.setConsoleWatching(payload.on);
|
|
32266
|
+
break;
|
|
31892
32267
|
default:
|
|
31893
32268
|
this.bus.log(`Unsupported channel: ${message.channel}`, "warn");
|
|
31894
32269
|
}
|
|
@@ -31933,7 +32308,7 @@ var WsClient = class {
|
|
|
31933
32308
|
if (perPromptLimit > 0) {
|
|
31934
32309
|
const estimated = estimateTokens(prompt);
|
|
31935
32310
|
if (estimated > perPromptLimit) {
|
|
31936
|
-
this.rejectPromptOverLimit(commandId, origin, estimated, perPromptLimit);
|
|
32311
|
+
this.rejectPromptOverLimit(commandId, origin, prompt, estimated, perPromptLimit);
|
|
31937
32312
|
return;
|
|
31938
32313
|
}
|
|
31939
32314
|
}
|
|
@@ -31948,7 +32323,7 @@ var WsClient = class {
|
|
|
31948
32323
|
verdict = { commandId, ok: false, reasons: ["unavailable"] };
|
|
31949
32324
|
}
|
|
31950
32325
|
if (!verdict.ok) {
|
|
31951
|
-
this.rejectByReview(commandId, origin, verdict.reasons);
|
|
32326
|
+
this.rejectByReview(commandId, origin, prompt, verdict.reasons);
|
|
31952
32327
|
return;
|
|
31953
32328
|
}
|
|
31954
32329
|
}
|
|
@@ -31978,6 +32353,8 @@ var WsClient = class {
|
|
|
31978
32353
|
commandId,
|
|
31979
32354
|
cmd: plan.cmd,
|
|
31980
32355
|
args: markerArgs,
|
|
32356
|
+
// The raw prompt so the web console echoes it like this TUI (ADR-0108), not "claude".
|
|
32357
|
+
prompt,
|
|
31981
32358
|
origin: "local"
|
|
31982
32359
|
});
|
|
31983
32360
|
}
|
|
@@ -32017,17 +32394,18 @@ var WsClient = class {
|
|
|
32017
32394
|
this.bus.push({ source: origin, kind: "out", text: resultBuf });
|
|
32018
32395
|
}
|
|
32019
32396
|
},
|
|
32020
|
-
onAttemptStart: (label, index, total) =>
|
|
32021
|
-
|
|
32022
|
-
kind: "log",
|
|
32023
|
-
|
|
32024
|
-
|
|
32025
|
-
|
|
32026
|
-
|
|
32027
|
-
|
|
32028
|
-
|
|
32029
|
-
|
|
32030
|
-
|
|
32397
|
+
onAttemptStart: (label, index, total) => {
|
|
32398
|
+
const text = `\u2192 trying ${plan.cmd} profile "${label}" (${index + 1}/${total})\u2026`;
|
|
32399
|
+
this.bus.push({ source: origin, kind: "log", text });
|
|
32400
|
+
this.send(WsChannels.COMMAND_OUTPUT, { commandId, seq: seq++, chunk: `${text}
|
|
32401
|
+
`, log: true });
|
|
32402
|
+
},
|
|
32403
|
+
onAttemptFail: (label, reason) => {
|
|
32404
|
+
const text = reason === "limit" ? `profile "${label}" hit its session limit \u2014 trying next` : `profile "${label}" failed to authenticate \u2014 trying next`;
|
|
32405
|
+
this.bus.push({ source: origin, kind: "log", text, level: "warn" });
|
|
32406
|
+
this.send(WsChannels.COMMAND_OUTPUT, { commandId, seq: seq++, chunk: `${text}
|
|
32407
|
+
`, log: true });
|
|
32408
|
+
}
|
|
32031
32409
|
});
|
|
32032
32410
|
this.bus.endBusy(plan.cmd);
|
|
32033
32411
|
const profileLabel = result.workedDir ? profileDisplayLabel(result.workedDir) : null;
|
|
@@ -32050,6 +32428,15 @@ var WsClient = class {
|
|
|
32050
32428
|
status: result.exitCode === 0 ? "done" : "failed",
|
|
32051
32429
|
exitCode: result.exitCode,
|
|
32052
32430
|
tokens: result.usage.tokens,
|
|
32431
|
+
// The ai_tokens split for this run (ADR-0145); attached only when we metered real tokens.
|
|
32432
|
+
...result.usage.tokens > 0 && {
|
|
32433
|
+
tokensBreakdown: {
|
|
32434
|
+
input: result.usage.input,
|
|
32435
|
+
output: result.usage.output,
|
|
32436
|
+
cacheRead: result.usage.cacheRead,
|
|
32437
|
+
cacheCreation: result.usage.cacheCreation
|
|
32438
|
+
}
|
|
32439
|
+
},
|
|
32053
32440
|
startedAt,
|
|
32054
32441
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
32055
32442
|
});
|
|
@@ -32058,7 +32445,16 @@ var WsClient = class {
|
|
|
32058
32445
|
{ metric: UsageMetric.COMMANDS, amount: 1, occurredAt }
|
|
32059
32446
|
];
|
|
32060
32447
|
if (result.exitCode === 0 && result.usage.tokens > 0) {
|
|
32061
|
-
events.push({
|
|
32448
|
+
events.push({
|
|
32449
|
+
metric: UsageMetric.AI_TOKENS,
|
|
32450
|
+
amount: result.usage.tokens,
|
|
32451
|
+
occurredAt,
|
|
32452
|
+
// The ai_tokens split so the server can persist + display it (ADR-0145).
|
|
32453
|
+
inputTokens: result.usage.input,
|
|
32454
|
+
outputTokens: result.usage.output,
|
|
32455
|
+
cacheReadTokens: result.usage.cacheRead,
|
|
32456
|
+
cacheCreationTokens: result.usage.cacheCreation
|
|
32457
|
+
});
|
|
32062
32458
|
this.bus.addTokens?.(result.usage.tokens);
|
|
32063
32459
|
}
|
|
32064
32460
|
this.reportUsage(events, profileLabel);
|
|
@@ -32093,7 +32489,7 @@ var WsClient = class {
|
|
|
32093
32489
|
* without spawning: surface it in the transcript + close the command on the server with a
|
|
32094
32490
|
* PROMPT_TOKEN_LIMIT_EXCEEDED note (exit 1). No usage is metered (nothing ran).
|
|
32095
32491
|
*/
|
|
32096
|
-
rejectPromptOverLimit(commandId, origin, estimated, limit) {
|
|
32492
|
+
rejectPromptOverLimit(commandId, origin, prompt, estimated, limit) {
|
|
32097
32493
|
const message = `[PROMPT_TOKEN_LIMIT_EXCEEDED] Prompt (~${estimated} tokens) exceeds the project per-prompt limit of ${limit} tokens.`;
|
|
32098
32494
|
logger.warn("command.ai.prompt-limit", { commandId, origin, estimated, limit });
|
|
32099
32495
|
this.bus.push({ source: origin, kind: "log", text: message, level: "error" });
|
|
@@ -32102,6 +32498,7 @@ var WsClient = class {
|
|
|
32102
32498
|
commandId,
|
|
32103
32499
|
cmd: "claude",
|
|
32104
32500
|
args: [],
|
|
32501
|
+
prompt,
|
|
32105
32502
|
origin: "local"
|
|
32106
32503
|
});
|
|
32107
32504
|
}
|
|
@@ -32119,7 +32516,7 @@ var WsClient = class {
|
|
|
32119
32516
|
* verdict + close the command on the server. `reasons` contains only violation categories
|
|
32120
32517
|
* (never secret values); a "unavailable" reason maps to no-outbound-available.
|
|
32121
32518
|
*/
|
|
32122
|
-
rejectByReview(commandId, origin, reasons) {
|
|
32519
|
+
rejectByReview(commandId, origin, prompt, reasons) {
|
|
32123
32520
|
const unavailable = reasons.includes("unavailable");
|
|
32124
32521
|
const code = unavailable ? "OUTBOUND_REVIEW_UNAVAILABLE" : "OUTBOUND_REVIEW_REJECTED";
|
|
32125
32522
|
const detail = reasons.filter((r) => r !== "unavailable").join(", ");
|
|
@@ -32131,6 +32528,7 @@ var WsClient = class {
|
|
|
32131
32528
|
commandId,
|
|
32132
32529
|
cmd: "claude",
|
|
32133
32530
|
args: [],
|
|
32531
|
+
prompt,
|
|
32134
32532
|
origin: "local"
|
|
32135
32533
|
});
|
|
32136
32534
|
}
|
|
@@ -32164,12 +32562,65 @@ var WsClient = class {
|
|
|
32164
32562
|
};
|
|
32165
32563
|
this.socket.send(JSON.stringify(envelope));
|
|
32166
32564
|
}
|
|
32565
|
+
/**
|
|
32566
|
+
* Console sync (ADR-0150): emit one incremental `console.sync` event (add/update/clear) — only
|
|
32567
|
+
* while a web Console viewer is attached (`consoleWatching`); each carries the next `rev` so the
|
|
32568
|
+
* web detects a gap. Dropped when disconnected — the next snapshot on (re)attach re-syncs.
|
|
32569
|
+
*/
|
|
32570
|
+
emitConsole(ev) {
|
|
32571
|
+
if (!this.consoleWatching) return;
|
|
32572
|
+
this.send(WsChannels.CONSOLE_SYNC, { ...ev, rev: ++this.consoleRev });
|
|
32573
|
+
}
|
|
32574
|
+
/** Send a full transcript snapshot (on attach + the periodic self-heal — ADR-0150). */
|
|
32575
|
+
sendConsoleSnapshot() {
|
|
32576
|
+
const entries2 = this.bus.snapshot().map(toDtoEntry);
|
|
32577
|
+
this.send(WsChannels.CONSOLE_SYNC, {
|
|
32578
|
+
kind: "snapshot",
|
|
32579
|
+
rev: ++this.consoleRev,
|
|
32580
|
+
entries: entries2
|
|
32581
|
+
});
|
|
32582
|
+
}
|
|
32583
|
+
/**
|
|
32584
|
+
* Handle `console.watch` (ADR-0150) — a renewable lease. `on` renews it: start mirroring on the
|
|
32585
|
+
* first assertion (fresh snapshot + arm the periodic snapshot); later renewals just extend the
|
|
32586
|
+
* lease (no re-snapshot). No renewal within `CONSOLE_WATCH_LEASE_MS` ⇒ the lease expires and
|
|
32587
|
+
* syncing stops. An explicit `on:false` stops immediately.
|
|
32588
|
+
*/
|
|
32589
|
+
setConsoleWatching(on) {
|
|
32590
|
+
if (!on) {
|
|
32591
|
+
this.stopConsoleSync();
|
|
32592
|
+
return;
|
|
32593
|
+
}
|
|
32594
|
+
if (this.consoleWatchExpiry) clearTimeout(this.consoleWatchExpiry);
|
|
32595
|
+
this.consoleWatchExpiry = setTimeout(() => this.stopConsoleSync(), CONSOLE_WATCH_LEASE_MS);
|
|
32596
|
+
this.consoleWatchExpiry.unref?.();
|
|
32597
|
+
if (this.consoleWatching) return;
|
|
32598
|
+
this.consoleWatching = true;
|
|
32599
|
+
this.sendConsoleSnapshot();
|
|
32600
|
+
this.consoleSnapshotTimer = setInterval(
|
|
32601
|
+
() => this.sendConsoleSnapshot(),
|
|
32602
|
+
CONSOLE_SNAPSHOT_INTERVAL_MS
|
|
32603
|
+
);
|
|
32604
|
+
this.consoleSnapshotTimer.unref?.();
|
|
32605
|
+
}
|
|
32606
|
+
/** Stop mirroring the transcript (lease expired / viewer left / reconnect) — clears both timers. */
|
|
32607
|
+
stopConsoleSync() {
|
|
32608
|
+
this.consoleWatching = false;
|
|
32609
|
+
if (this.consoleSnapshotTimer) {
|
|
32610
|
+
clearInterval(this.consoleSnapshotTimer);
|
|
32611
|
+
this.consoleSnapshotTimer = null;
|
|
32612
|
+
}
|
|
32613
|
+
if (this.consoleWatchExpiry) {
|
|
32614
|
+
clearTimeout(this.consoleWatchExpiry);
|
|
32615
|
+
this.consoleWatchExpiry = null;
|
|
32616
|
+
}
|
|
32617
|
+
}
|
|
32167
32618
|
/**
|
|
32168
32619
|
* Send a cli → server request and await the reply by envelope id (15s timeout).
|
|
32169
32620
|
* Used for quota.check before spawning an AI cli (ADR-0020).
|
|
32170
32621
|
*/
|
|
32171
32622
|
request(channel, data) {
|
|
32172
|
-
return new Promise((
|
|
32623
|
+
return new Promise((resolve7, reject) => {
|
|
32173
32624
|
if (!this.socket || !this.sessionKey) {
|
|
32174
32625
|
reject(new Error("Not connected to the server."));
|
|
32175
32626
|
return;
|
|
@@ -32189,7 +32640,7 @@ var WsClient = class {
|
|
|
32189
32640
|
}, 15e3);
|
|
32190
32641
|
timer.unref?.();
|
|
32191
32642
|
this.pending.set(envelope.id, {
|
|
32192
|
-
resolve: (value) =>
|
|
32643
|
+
resolve: (value) => resolve7(value),
|
|
32193
32644
|
timer
|
|
32194
32645
|
});
|
|
32195
32646
|
this.socket.send(JSON.stringify(envelope));
|
|
@@ -32236,7 +32687,7 @@ var WsClient = class {
|
|
|
32236
32687
|
/** Create the physic folder at an absolute path if missing (best-effort). */
|
|
32237
32688
|
ensurePhysicFolderPath(folder) {
|
|
32238
32689
|
try {
|
|
32239
|
-
if (!
|
|
32690
|
+
if (!existsSync8(folder)) {
|
|
32240
32691
|
mkdirSync4(folder, { recursive: true });
|
|
32241
32692
|
this.bus.log(`Created physic project folder: ${folder}`);
|
|
32242
32693
|
}
|
|
@@ -32251,7 +32702,7 @@ var WsClient = class {
|
|
|
32251
32702
|
*/
|
|
32252
32703
|
physicFolderPath(projectName) {
|
|
32253
32704
|
const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
|
|
32254
|
-
return safe ?
|
|
32705
|
+
return safe ? join15(this.context.profileDir, safe) : null;
|
|
32255
32706
|
}
|
|
32256
32707
|
/**
|
|
32257
32708
|
* Report machine.status: version + the state of the physic project being served
|
|
@@ -32265,8 +32716,9 @@ var WsClient = class {
|
|
|
32265
32716
|
projects: [],
|
|
32266
32717
|
version: CLI_VERSION,
|
|
32267
32718
|
physicPath,
|
|
32268
|
-
physicPathExists: physicPath ?
|
|
32269
|
-
|
|
32719
|
+
physicPathExists: physicPath ? existsSync8(physicPath) : void 0,
|
|
32720
|
+
// Real coarse state (ADR-0152): !paused and a recent tick (was hard-coded false).
|
|
32721
|
+
autonomousRunning: physicPath ? isAutonomousRunning(physicPath) : false
|
|
32270
32722
|
});
|
|
32271
32723
|
}
|
|
32272
32724
|
/** Heartbeat every 30s: ping + periodically re-report machine.status. */
|
|
@@ -32336,7 +32788,7 @@ var WsClient = class {
|
|
|
32336
32788
|
*/
|
|
32337
32789
|
uploadLog() {
|
|
32338
32790
|
try {
|
|
32339
|
-
const upload = readLogUpload(
|
|
32791
|
+
const upload = readLogUpload(join15(this.context.profileDir, "logs"));
|
|
32340
32792
|
if (!upload) return;
|
|
32341
32793
|
const sig = `${upload.fileName}:${upload.totalBytes}`;
|
|
32342
32794
|
if (sig === this.lastLogSig) return;
|
|
@@ -32364,10 +32816,10 @@ var WsClient = class {
|
|
|
32364
32816
|
};
|
|
32365
32817
|
|
|
32366
32818
|
// src/core/instance-lock.ts
|
|
32367
|
-
import { readFileSync as
|
|
32368
|
-
import { join as
|
|
32819
|
+
import { readFileSync as readFileSync14, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
32820
|
+
import { join as join16 } from "path";
|
|
32369
32821
|
function lockPath(profileDir2) {
|
|
32370
|
-
return
|
|
32822
|
+
return join16(profileDir2, "start.lock");
|
|
32371
32823
|
}
|
|
32372
32824
|
function isAlive(pid) {
|
|
32373
32825
|
try {
|
|
@@ -32379,7 +32831,7 @@ function isAlive(pid) {
|
|
|
32379
32831
|
}
|
|
32380
32832
|
function lockHolder(profileDir2) {
|
|
32381
32833
|
try {
|
|
32382
|
-
const pid = Number.parseInt(
|
|
32834
|
+
const pid = Number.parseInt(readFileSync14(lockPath(profileDir2), "utf8").trim(), 10);
|
|
32383
32835
|
if (Number.isInteger(pid) && pid > 0 && isAlive(pid)) return pid;
|
|
32384
32836
|
} catch {
|
|
32385
32837
|
}
|
|
@@ -32392,7 +32844,7 @@ function acquireInstanceLock(profileDir2) {
|
|
|
32392
32844
|
}
|
|
32393
32845
|
function releaseInstanceLock(profileDir2) {
|
|
32394
32846
|
try {
|
|
32395
|
-
const pid = Number.parseInt(
|
|
32847
|
+
const pid = Number.parseInt(readFileSync14(lockPath(profileDir2), "utf8").trim(), 10);
|
|
32396
32848
|
if (pid === process.pid) rmSync5(lockPath(profileDir2), { force: true });
|
|
32397
32849
|
} catch {
|
|
32398
32850
|
}
|
|
@@ -32441,6 +32893,24 @@ var SessionBus = class {
|
|
|
32441
32893
|
this.emitter.emit("transcript", entry);
|
|
32442
32894
|
return entry.id;
|
|
32443
32895
|
}
|
|
32896
|
+
/**
|
|
32897
|
+
* Wipe the transcript (`/clear` + idle auto-clear — ADR-0057): empty the replay history and
|
|
32898
|
+
* notify consumers so the TUI clears the screen and the console-sync emitter (ADR-0150) sends
|
|
32899
|
+
* a `clear` event. The current snapshot afterwards is empty (until new lines arrive).
|
|
32900
|
+
*/
|
|
32901
|
+
clear() {
|
|
32902
|
+
this.history.length = 0;
|
|
32903
|
+
this.emitter.emit("clear");
|
|
32904
|
+
}
|
|
32905
|
+
/** Subscribe to transcript clears (TUI screen wipe + console.sync `clear` — ADR-0150). */
|
|
32906
|
+
onClear(fn) {
|
|
32907
|
+
this.emitter.on("clear", fn);
|
|
32908
|
+
return () => this.emitter.off("clear", fn);
|
|
32909
|
+
}
|
|
32910
|
+
/** The current transcript entries (a copy) — the console-sync snapshot source (ADR-0150). */
|
|
32911
|
+
snapshot() {
|
|
32912
|
+
return [...this.history];
|
|
32913
|
+
}
|
|
32444
32914
|
/**
|
|
32445
32915
|
* Producer → consumers: replace the text of an already-pushed entry (ADR-0108) — used to
|
|
32446
32916
|
* grow a streaming `result` block in place without pushing a new line per chunk. No-op if the
|
|
@@ -37876,8 +38346,8 @@ var Ink = class {
|
|
|
37876
38346
|
}
|
|
37877
38347
|
}
|
|
37878
38348
|
async waitUntilExit() {
|
|
37879
|
-
this.exitPromise ||= new Promise((
|
|
37880
|
-
this.resolveExitPromise =
|
|
38349
|
+
this.exitPromise ||= new Promise((resolve7, reject) => {
|
|
38350
|
+
this.resolveExitPromise = resolve7;
|
|
37881
38351
|
this.rejectExitPromise = reject;
|
|
37882
38352
|
});
|
|
37883
38353
|
return this.exitPromise;
|
|
@@ -38244,17 +38714,17 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
38244
38714
|
var import_react26 = __toESM(require_react(), 1);
|
|
38245
38715
|
|
|
38246
38716
|
// src/core/input-history.ts
|
|
38247
|
-
import { existsSync as
|
|
38248
|
-
import { join as
|
|
38717
|
+
import { existsSync as existsSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync7 } from "fs";
|
|
38718
|
+
import { join as join17 } from "path";
|
|
38249
38719
|
var MAX_HISTORY = 200;
|
|
38250
38720
|
function historyPath(profileDir2) {
|
|
38251
|
-
return
|
|
38721
|
+
return join17(profileDir2, "input-history.json");
|
|
38252
38722
|
}
|
|
38253
38723
|
function loadInputHistory(profileDir2) {
|
|
38254
38724
|
const path = historyPath(profileDir2);
|
|
38255
|
-
if (!
|
|
38725
|
+
if (!existsSync10(path)) return [];
|
|
38256
38726
|
try {
|
|
38257
|
-
const data = JSON.parse(
|
|
38727
|
+
const data = JSON.parse(readFileSync16(path, "utf8"));
|
|
38258
38728
|
return Array.isArray(data) ? data.filter((x) => typeof x === "string") : [];
|
|
38259
38729
|
} catch {
|
|
38260
38730
|
return [];
|
|
@@ -38514,8 +38984,8 @@ function TranscriptLine({ line }) {
|
|
|
38514
38984
|
var import_react24 = __toESM(require_react(), 1);
|
|
38515
38985
|
|
|
38516
38986
|
// src/ui/slash-commands.ts
|
|
38517
|
-
import { existsSync as
|
|
38518
|
-
import { join as
|
|
38987
|
+
import { existsSync as existsSync11 } from "fs";
|
|
38988
|
+
import { join as join18 } from "path";
|
|
38519
38989
|
var CONFIG_KEYS = [
|
|
38520
38990
|
"aiCli",
|
|
38521
38991
|
"claudeHome",
|
|
@@ -38527,7 +38997,7 @@ var CONFIG_KEYS = [
|
|
|
38527
38997
|
"aiEnv"
|
|
38528
38998
|
];
|
|
38529
38999
|
function configPath(profileDir2) {
|
|
38530
|
-
return
|
|
39000
|
+
return join18(profileDir2, "config.json");
|
|
38531
39001
|
}
|
|
38532
39002
|
function parseConfigAssignment(key, valueTokens) {
|
|
38533
39003
|
const value = valueTokens.join(" ").trim();
|
|
@@ -38577,7 +39047,7 @@ function runConfig(ctx) {
|
|
|
38577
39047
|
ctx.print(`\u2714 ${verb} default config: ${path}`);
|
|
38578
39048
|
ctx.print("add claude profiles: /config set claudeHome .claude .claude-1 (tried in order)");
|
|
38579
39049
|
};
|
|
38580
|
-
if (
|
|
39050
|
+
if (existsSync11(path)) {
|
|
38581
39051
|
ctx.confirm(
|
|
38582
39052
|
`config exists: ${path} \u2014 replace it with defaults? (y/N)`,
|
|
38583
39053
|
() => writeDefaults("replaced with")
|
|
@@ -38662,7 +39132,7 @@ function runOutput(ctx) {
|
|
|
38662
39132
|
}
|
|
38663
39133
|
function runLogs(ctx) {
|
|
38664
39134
|
const n = Math.max(1, Math.min(200, Number(ctx.args[0]) || 20));
|
|
38665
|
-
const dir2 =
|
|
39135
|
+
const dir2 = join18(ctx.info.profileDir, "logs");
|
|
38666
39136
|
const lines = readRecentLogLines(dir2, n);
|
|
38667
39137
|
if (lines.length === 0) {
|
|
38668
39138
|
ctx.print("No log lines yet.");
|
|
@@ -38986,11 +39456,29 @@ function App2({
|
|
|
38986
39456
|
const [history, setHistory] = (0, import_react26.useState)(() => loadInputHistory(info.profileDir));
|
|
38987
39457
|
const [startedAt] = (0, import_react26.useState)(() => Date.now());
|
|
38988
39458
|
const [pendingConfirm, setPendingConfirm] = (0, import_react26.useState)(null);
|
|
39459
|
+
const [autoClearIdleMs] = (0, import_react26.useState)(() => {
|
|
39460
|
+
const min = readProfileConfig(info.profileDir).autoClearIdleMinutes ?? 10;
|
|
39461
|
+
return min > 0 ? min * 6e4 : 0;
|
|
39462
|
+
});
|
|
39463
|
+
const lastActivityRef = (0, import_react26.useRef)(startedAt);
|
|
39464
|
+
const busyRef = (0, import_react26.useRef)(busy);
|
|
39465
|
+
busyRef.current = busy;
|
|
39466
|
+
const entriesLenRef = (0, import_react26.useRef)(entries2.length);
|
|
39467
|
+
entriesLenRef.current = entries2.length;
|
|
39468
|
+
const pendingConfirmRef = (0, import_react26.useRef)(pendingConfirm);
|
|
39469
|
+
pendingConfirmRef.current = pendingConfirm;
|
|
39470
|
+
const clearTranscript = (0, import_react26.useCallback)(() => {
|
|
39471
|
+
setEntries([]);
|
|
39472
|
+
setScrollBack(0);
|
|
39473
|
+
stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
39474
|
+
bus.clear();
|
|
39475
|
+
}, [stdout, bus]);
|
|
38989
39476
|
(0, import_react26.useEffect)(() => {
|
|
38990
39477
|
const offTranscript = bus.onTranscript((entry) => {
|
|
38991
39478
|
setEntries(
|
|
38992
39479
|
(prev) => prev.length >= ENTRY_CAP ? [...prev.slice(prev.length - ENTRY_CAP + 1), entry] : [...prev, entry]
|
|
38993
39480
|
);
|
|
39481
|
+
if (entry.source !== "system") lastActivityRef.current = Date.now();
|
|
38994
39482
|
if (entry.kind === "aireq" || entry.kind === "aires") setExpanded(/* @__PURE__ */ new Set());
|
|
38995
39483
|
});
|
|
38996
39484
|
const offUpdate = bus.onTranscriptUpdate(
|
|
@@ -39017,6 +39505,21 @@ function App2({
|
|
|
39017
39505
|
offTokens();
|
|
39018
39506
|
};
|
|
39019
39507
|
}, [bus]);
|
|
39508
|
+
(0, import_react26.useEffect)(() => {
|
|
39509
|
+
if (autoClearIdleMs <= 0) return;
|
|
39510
|
+
const timer = setInterval(() => {
|
|
39511
|
+
if (busyRef.current !== null || pendingConfirmRef.current || entriesLenRef.current === 0) return;
|
|
39512
|
+
if (Date.now() - lastActivityRef.current < autoClearIdleMs) return;
|
|
39513
|
+
clearTranscript();
|
|
39514
|
+
bus.push({
|
|
39515
|
+
source: "system",
|
|
39516
|
+
kind: "log",
|
|
39517
|
+
text: `Transcript auto-cleared after ${Math.round(autoClearIdleMs / 6e4)} min idle.`
|
|
39518
|
+
});
|
|
39519
|
+
lastActivityRef.current = Date.now();
|
|
39520
|
+
}, 6e4);
|
|
39521
|
+
return () => clearInterval(timer);
|
|
39522
|
+
}, [autoClearIdleMs, bus, clearTranscript]);
|
|
39020
39523
|
const bodyHeight = Math.max(3, rows - CHROME_ROWS);
|
|
39021
39524
|
const textWidth = Math.max(10, cols - 1 - 8);
|
|
39022
39525
|
const lines = (0, import_react26.useMemo)(() => flattenEntries(entries2, textWidth, expanded), [entries2, textWidth, expanded]);
|
|
@@ -39042,6 +39545,7 @@ function App2({
|
|
|
39042
39545
|
};
|
|
39043
39546
|
const handleSubmit = (input) => {
|
|
39044
39547
|
setScrollBack(0);
|
|
39548
|
+
lastActivityRef.current = Date.now();
|
|
39045
39549
|
const trimmed = input.trim();
|
|
39046
39550
|
if (pendingConfirm) {
|
|
39047
39551
|
const { onYes } = pendingConfirm;
|
|
@@ -39057,11 +39561,7 @@ function App2({
|
|
|
39057
39561
|
runSlashCommand(trimmed, {
|
|
39058
39562
|
info,
|
|
39059
39563
|
print: (text, level) => bus.push({ source: "local", kind: "log", text, level }),
|
|
39060
|
-
clear:
|
|
39061
|
-
setEntries([]);
|
|
39062
|
-
setScrollBack(0);
|
|
39063
|
-
stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
39064
|
-
},
|
|
39564
|
+
clear: clearTranscript,
|
|
39065
39565
|
quit: () => exit(),
|
|
39066
39566
|
confirm: (prompt, onYes) => {
|
|
39067
39567
|
bus.push({ source: "local", kind: "log", text: prompt });
|
|
@@ -39133,7 +39633,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
39133
39633
|
return;
|
|
39134
39634
|
}
|
|
39135
39635
|
logger.configure({
|
|
39136
|
-
dir:
|
|
39636
|
+
dir: join19(profileDir2, "logs"),
|
|
39137
39637
|
level: resolveLogLevel(),
|
|
39138
39638
|
base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
|
|
39139
39639
|
});
|
|
@@ -39153,7 +39653,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
39153
39653
|
if (result.action === "updated") {
|
|
39154
39654
|
logger.info("update.updated", { version: result.version });
|
|
39155
39655
|
console.log("Restarting with the new version\u2026");
|
|
39156
|
-
const child =
|
|
39656
|
+
const child = spawn5(process.execPath, process.argv.slice(1), {
|
|
39157
39657
|
stdio: "inherit",
|
|
39158
39658
|
env: { ...process.env, FOURPM_NO_UPDATE: "1" }
|
|
39159
39659
|
});
|
|
@@ -39191,7 +39691,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
39191
39691
|
logger.error("ws.run.crash", { message: e.message, stack: e.stack });
|
|
39192
39692
|
if (client.isStopped) return;
|
|
39193
39693
|
bus.log(`Recovered from an internal error \u2014 reconnecting: ${e.message}`, "error");
|
|
39194
|
-
await new Promise((
|
|
39694
|
+
await new Promise((resolve7) => setTimeout(resolve7, 1e3));
|
|
39195
39695
|
}
|
|
39196
39696
|
}
|
|
39197
39697
|
};
|
|
@@ -39318,10 +39818,10 @@ function Picker({
|
|
|
39318
39818
|
] });
|
|
39319
39819
|
}
|
|
39320
39820
|
function selectFromList(items, title) {
|
|
39321
|
-
return new Promise((
|
|
39821
|
+
return new Promise((resolve7) => {
|
|
39322
39822
|
let chosen = null;
|
|
39323
39823
|
const app = render_default(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Picker, { items, title, onDone: (v) => chosen = v }));
|
|
39324
|
-
void app.waitUntilExit().then(() =>
|
|
39824
|
+
void app.waitUntilExit().then(() => resolve7(chosen));
|
|
39325
39825
|
});
|
|
39326
39826
|
}
|
|
39327
39827
|
|