@buzzni/saycode-cli 0.3.2 → 0.3.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/index.mjs +534 -65
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -40,7 +40,7 @@ function _mergeNamespaces(n, m) {
|
|
|
40
40
|
}
|
|
41
41
|
return Object.freeze(Object.defineProperty(n, Symbol.toStringTag, { value: "Module" }));
|
|
42
42
|
}
|
|
43
|
-
const version = "0.3.
|
|
43
|
+
const version = "0.3.3";
|
|
44
44
|
const manifest = {
|
|
45
45
|
version
|
|
46
46
|
};
|
|
@@ -58,13 +58,13 @@ function parseAgentControlArgs(verb, target, rest, hasPositionalTarget) {
|
|
|
58
58
|
return null;
|
|
59
59
|
}
|
|
60
60
|
const AGENT_VERBS = ["whoami", "ls", "read", "wait", "prompt", "spawn", ...AGENT_CONTROL_VERBS];
|
|
61
|
-
function fail(message) {
|
|
61
|
+
function fail$1(message) {
|
|
62
62
|
return { command: "error", message };
|
|
63
63
|
}
|
|
64
64
|
function parseAgentArgs(flags, positionals) {
|
|
65
65
|
const [verb, ...rest] = positionals;
|
|
66
66
|
if (!verb || !AGENT_VERBS.includes(verb)) {
|
|
67
|
-
return fail(`agent needs one of: ${AGENT_VERBS.join(", ")}`);
|
|
67
|
+
return fail$1(`agent needs one of: ${AGENT_VERBS.join(", ")}`);
|
|
68
68
|
}
|
|
69
69
|
const timeout = flags.timeoutMs !== void 0 ? { timeoutMs: flags.timeoutMs } : {};
|
|
70
70
|
const hasPositionalTarget = flags.sessionId === void 0 && rest[0] !== void 0;
|
|
@@ -77,18 +77,18 @@ function parseAgentArgs(flags, positionals) {
|
|
|
77
77
|
case "ls":
|
|
78
78
|
return { command: "agent", verb: "ls" };
|
|
79
79
|
case "read": {
|
|
80
|
-
if (!target) return fail("agent read needs a session id (see `saycode agent ls`)");
|
|
80
|
+
if (!target) return fail$1("agent read needs a session id (see `saycode agent ls`)");
|
|
81
81
|
return { command: "agent", verb: "read", sessionId: target, ...flags.last !== void 0 ? { last: flags.last } : {} };
|
|
82
82
|
}
|
|
83
83
|
case "wait": {
|
|
84
|
-
if (!target) return fail("agent wait needs a session id (see `saycode agent ls`)");
|
|
85
|
-
if (!flags.until) return fail("agent wait needs --until turn-end|waiting-input|completed");
|
|
84
|
+
if (!target) return fail$1("agent wait needs a session id (see `saycode agent ls`)");
|
|
85
|
+
if (!flags.until) return fail$1("agent wait needs --until turn-end|waiting-input|completed");
|
|
86
86
|
return { command: "agent", verb: "wait", sessionId: target, until: flags.until, ...timeout };
|
|
87
87
|
}
|
|
88
88
|
case "prompt": {
|
|
89
|
-
if (!target) return fail("agent prompt needs a session id (see `saycode agent ls`)");
|
|
89
|
+
if (!target) return fail$1("agent prompt needs a session id (see `saycode agent ls`)");
|
|
90
90
|
const text2 = rest.slice(hasPositionalTarget ? 1 : 0).join(" ").trim();
|
|
91
|
-
if (!text2) return fail("agent prompt needs the text to send");
|
|
91
|
+
if (!text2) return fail$1("agent prompt needs the text to send");
|
|
92
92
|
return {
|
|
93
93
|
command: "agent",
|
|
94
94
|
verb: "prompt",
|
|
@@ -104,16 +104,16 @@ function parseAgentArgs(flags, positionals) {
|
|
|
104
104
|
const files = flags.files ?? [];
|
|
105
105
|
if (flags.handoff) {
|
|
106
106
|
if (flags.prompt !== void 0) {
|
|
107
|
-
return fail("agent spawn --handoff sends the conversation, so it cannot also take --prompt");
|
|
107
|
+
return fail$1("agent spawn --handoff sends the conversation, so it cannot also take --prompt");
|
|
108
108
|
}
|
|
109
109
|
if (files.length > 0) {
|
|
110
|
-
return fail("agent spawn --handoff sends the conversation, so it cannot also take --file");
|
|
110
|
+
return fail$1("agent spawn --handoff sends the conversation, so it cannot also take --file");
|
|
111
111
|
}
|
|
112
112
|
const mode = flags.mode?.trim() || "summary";
|
|
113
113
|
if (mode === "full") {
|
|
114
|
-
return fail("agent spawn --handoff does not support --mode full yet; only summary is available");
|
|
114
|
+
return fail$1("agent spawn --handoff does not support --mode full yet; only summary is available");
|
|
115
115
|
}
|
|
116
|
-
if (mode !== "summary") return fail(`agent spawn --handoff got an unknown --mode: ${mode}`);
|
|
116
|
+
if (mode !== "summary") return fail$1(`agent spawn --handoff got an unknown --mode: ${mode}`);
|
|
117
117
|
const from = flags.from?.trim();
|
|
118
118
|
return {
|
|
119
119
|
command: "agent",
|
|
@@ -125,7 +125,7 @@ function parseAgentArgs(flags, positionals) {
|
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
127
|
const text2 = flags.prompt?.trim();
|
|
128
|
-
if (!text2) return fail("agent spawn needs --prompt <text>");
|
|
128
|
+
if (!text2) return fail$1("agent spawn needs --prompt <text>");
|
|
129
129
|
return {
|
|
130
130
|
command: "agent",
|
|
131
131
|
verb: "spawn",
|
|
@@ -137,7 +137,7 @@ function parseAgentArgs(flags, positionals) {
|
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
139
|
default:
|
|
140
|
-
return fail(`unsupported agent verb: ${verb}`);
|
|
140
|
+
return fail$1(`unsupported agent verb: ${verb}`);
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
function fencedMarkdownRanges(content) {
|
|
@@ -816,12 +816,21 @@ const extensionPanelKo = {
|
|
|
816
816
|
};
|
|
817
817
|
const extensionInstallKo = {
|
|
818
818
|
"extensions.installOfficial": "확장 설치",
|
|
819
|
+
"extensions.installArtifactPublisher": "공개 링크 확장 설치",
|
|
819
820
|
"extensions.installDescription": "계속하려면 공식 확장을 설치하세요.",
|
|
820
821
|
"extensions.machineExecutePermission": "이 확장은 미리 정의된 동작에만 machine.execute 권한을 요청합니다.",
|
|
821
822
|
"extensions.notInstalled": "요청한 확장이 설치되지 않았습니다.",
|
|
822
823
|
"extensions.disabled": "요청한 확장이 비활성화되어 있습니다.",
|
|
823
824
|
"extensions.quarantined": "요청한 확장이 반복된 오류로 격리되었습니다.",
|
|
824
|
-
"extensions.unavailable": "요청한 확장이 설치되어 있지만 현재 사용할 수 없습니다."
|
|
825
|
+
"extensions.unavailable": "요청한 확장이 설치되어 있지만 현재 사용할 수 없습니다.",
|
|
826
|
+
"extensions.permission.artifactPublishPublic": "선택한 결과물의 공개 링크를 만들 수 있습니다. 파일 내용과 링크 처리는 Saycode Core가 담당합니다.",
|
|
827
|
+
"artifactPublication.dialogTitle": "공개 링크",
|
|
828
|
+
"artifactPublication.copy": "링크 복사",
|
|
829
|
+
"artifactPublication.copied": "복사됨",
|
|
830
|
+
"artifactPublication.open": "링크 열기",
|
|
831
|
+
"artifactPublication.revoke": "공개 중지",
|
|
832
|
+
"artifactPublication.revoking": "중지 중…",
|
|
833
|
+
"artifactPublication.failed": "공개 링크 작업에 실패했습니다."
|
|
825
834
|
};
|
|
826
835
|
const personalChatsLandingKo = {
|
|
827
836
|
"personalChats.heroTitle": "무엇을 도와드릴까요?",
|
|
@@ -1132,7 +1141,9 @@ const machineSettingsConsoleKo = {
|
|
|
1132
1141
|
"machineConsole.updateTimeout": "업데이트가 시작됐지만 완료를 확인하지 못했습니다. 잠시 후 다시 진단해 주세요.",
|
|
1133
1142
|
"machineConsole.updateFailed": "CLI 업데이트에 실패했습니다.",
|
|
1134
1143
|
"machineConsole.aiCliStarted": "Codex와 Claude Code 설치를 시작했습니다.",
|
|
1135
|
-
"machineConsole.aiCliFailed": "AI CLI 설치를 시작하지 못했습니다."
|
|
1144
|
+
"machineConsole.aiCliFailed": "AI CLI 설치를 시작하지 못했습니다.",
|
|
1145
|
+
"machineConsole.machineActionFailed": "머신 작업에 실패했습니다.",
|
|
1146
|
+
"machineConsole.extensionCommandsUnavailable": "Extension 명령을 사용할 수 없습니다."
|
|
1136
1147
|
};
|
|
1137
1148
|
const sessionTokenUsageKo = {
|
|
1138
1149
|
"sessionTokenUsage.total": "누적 {count} 토큰",
|
|
@@ -1260,7 +1271,12 @@ const additionalDirectoriesKo = {
|
|
|
1260
1271
|
"additionalDirectories.limitReached": "추가 디렉터리는 최대 8개까지 등록할 수 있습니다.",
|
|
1261
1272
|
"additionalDirectories.machineMismatch": "현재 머신과 다른 위치에서 선택한 디렉터리입니다.",
|
|
1262
1273
|
"additionalDirectories.saveFailed": "디렉터리를 등록하지 못했습니다.",
|
|
1263
|
-
"additionalDirectories.folderLoadFailed": "디렉터리를 불러오지 못했습니다."
|
|
1274
|
+
"additionalDirectories.folderLoadFailed": "디렉터리를 불러오지 못했습니다.",
|
|
1275
|
+
"additionalDirectories.relatedMountMissing": "관련 프로젝트 디렉터리 {count}개를 자동 마운트에서 제외하고 세션을 시작했습니다.",
|
|
1276
|
+
"additionalDirectories.relatedMountLinkLookupFailed": "관련 프로젝트 목록을 불러오지 못해 자동 마운트 없이 세션을 시작했습니다.",
|
|
1277
|
+
"additionalDirectories.relatedMountLookupFailed": "관련 프로젝트 {count}개를 불러오지 못해 제외하고 세션을 시작했습니다.",
|
|
1278
|
+
"additionalDirectories.relatedMountRuntimeUnsupported": "이 머신의 Happy CLI가 관련 프로젝트 자동 마운트를 지원하지 않아 기본 세션으로 시작했습니다.",
|
|
1279
|
+
"additionalDirectories.relatedMountLimit": "관련 프로젝트 디렉터리 상한을 넘어 {count}개를 제외하고 세션을 시작했습니다."
|
|
1264
1280
|
};
|
|
1265
1281
|
const agentHandoffKo = {
|
|
1266
1282
|
"agentHandoff.open": "다른 에이전트로 이어가기",
|
|
@@ -1332,6 +1348,15 @@ const subagentKo = {
|
|
|
1332
1348
|
const reviewNavigationKo = {
|
|
1333
1349
|
"review.action.openSourceSession": "원본 대화로 이동: {title}"
|
|
1334
1350
|
};
|
|
1351
|
+
const chatFolderUploadKo = {
|
|
1352
|
+
"upload.kindFolder": "폴더",
|
|
1353
|
+
"upload.folderReference": "{path} ({count}개 파일)",
|
|
1354
|
+
"upload.folderChip": "{name} · {count}개 파일",
|
|
1355
|
+
"upload.folderEmpty": "폴더에 업로드할 파일이 없습니다: {name}",
|
|
1356
|
+
"upload.progress": "파일 업로드 중 {done}/{total}",
|
|
1357
|
+
"upload.partialIssue": "업로드 실패 {failed}개 · 건너뜀 {skipped}개",
|
|
1358
|
+
"upload.invalidPath": "안전하지 않은 첨부 경로입니다."
|
|
1359
|
+
};
|
|
1335
1360
|
const aiCommitPrKo = {
|
|
1336
1361
|
"aiCommitPr.stagedCount": "스테이지된 파일 {count}개",
|
|
1337
1362
|
"aiCommitPr.noStaged": "스테이지된 변경이 없습니다.",
|
|
@@ -1404,7 +1429,14 @@ const projectLinksKo = {
|
|
|
1404
1429
|
"projectLinks.mcpToggleHint": "켜면 AI가 관련 프로젝트로 작업을 제안했을 때 확인 없이 바로 진행됩니다.",
|
|
1405
1430
|
"projectLinks.mcpToggleFailed": "설정을 바꾸지 못했습니다."
|
|
1406
1431
|
};
|
|
1407
|
-
const
|
|
1432
|
+
const connectorReadinessKo = {
|
|
1433
|
+
"connectorReadiness.badge": "커넥터 {count}건 확인 필요",
|
|
1434
|
+
"connectorReadiness.state.needsAuth": "다시 연결 필요",
|
|
1435
|
+
"connectorReadiness.state.runtimeFailed": "연결 실패",
|
|
1436
|
+
"connectorReadiness.state.configMissing": "설정 없음",
|
|
1437
|
+
"connectorReadiness.state.unknown": "상태 미확인"
|
|
1438
|
+
};
|
|
1439
|
+
const catalogExtensionsKo = { ...taskNotificationKo, ...machineUsageKo, ...completionInboxKo, ...agentBoardKo, ...fanoutLifecycleKo, ...conversationSearchKo, ...browserCookieImportKo, ...orcaPortKo, ...usageAnalyticsKo, ...outboundWebhookKo, ...scheduledAutomationsKo, ...githubTriggersKo, ...memoryHubKo, ...extensionPanelKo, ...extensionInstallKo, ...personalChatsKo, ...newSessionStarterKo, ...browserConsoleCaptureKo, ...completionSignalKo, ...agentLoopDetectionKo, ...operationModelRetryKo, ...skillCatalogKo, ...ciAutoFixKo, ...collaborationKo, ...backgroundTaskKo, ...sessionTitleKo, ...accountSettingsKo, ...projectScopeKo, ...machineSettingsConsoleKo, ...sessionTokenUsageKo, ...worktreeSetupKo, ...connectionIndicatorKo, ...voiceDictationKo, ...newSessionProfileKo, ...agentBoardBroadcastKo, ...judgementEvidenceKo, ...releaseNoteKo, ...notificationSoundKo, ...officeViewerKo, ...additionalDirectoriesKo, ...agentHandoffKo, ...sidebarUpdateKo, ...autopilotSelfReviewRoundsKo, ...aiCommitPrKo, ...externalWorktreeInboxKo, ...externalSessionVaultKo, ...terminalFontKo, ...globalPauseKo, ...subagentKo, ...reviewNavigationKo, ...projectLinksKo, ...projectHandoffKo, ...chatFolderUploadKo, ...connectorReadinessKo };
|
|
1408
1440
|
const onboardingKo = {
|
|
1409
1441
|
"onboarding.dialogAria": "시작 설정",
|
|
1410
1442
|
"onboarding.eyebrow": "처음 시작하기",
|
|
@@ -1500,6 +1532,44 @@ const ko = {
|
|
|
1500
1532
|
"common.reset": "초기화",
|
|
1501
1533
|
"common.cancel": "취소",
|
|
1502
1534
|
"common.close": "닫기",
|
|
1535
|
+
"networkDataSaver.transferConfirm": "{operation} 작업은 데이터를 많이 사용할 수 있습니다. 예상 크기: {size}. 계속할까요?",
|
|
1536
|
+
"networkDataSaver.sizeUnknown": "알 수 없음",
|
|
1537
|
+
"networkDataSaver.operation.update-download": "업데이트 다운로드",
|
|
1538
|
+
"networkDataSaver.operation.file-upload": "파일 업로드",
|
|
1539
|
+
"networkDataSaver.operation.folder-upload": "폴더 업로드",
|
|
1540
|
+
"networkDataSaver.operation.file-download": "파일 다운로드",
|
|
1541
|
+
"networkDataSaver.operation.project-deploy": "프로젝트 배포",
|
|
1542
|
+
"networkDataSaver.operation.project-import": "프로젝트 가져오기",
|
|
1543
|
+
"networkDataSaver.title": "네트워크 데이터 절약",
|
|
1544
|
+
"networkDataSaver.description": "macOS가 비용이 들거나 저데이터 모드인 경로로 판단하면 백그라운드 데이터 사용을 자동으로 줄입니다.",
|
|
1545
|
+
"networkDataSaver.loading": "네트워크 상태를 확인하는 중…",
|
|
1546
|
+
"networkDataSaver.unsupported": "이 앱 버전에서는 네트워크 비용 상태를 확인할 수 없습니다.",
|
|
1547
|
+
"networkDataSaver.status.active": "데이터 절약 중",
|
|
1548
|
+
"networkDataSaver.status.inactive": "데이터 절약 꺼짐",
|
|
1549
|
+
"networkDataSaver.modeLabel": "절약 방식",
|
|
1550
|
+
"networkDataSaver.mode.automatic": "자동",
|
|
1551
|
+
"networkDataSaver.mode.always-save": "항상 절약",
|
|
1552
|
+
"networkDataSaver.mode.never-save": "절약 안 함",
|
|
1553
|
+
"networkDataSaver.interfaceLabel": "현재 연결",
|
|
1554
|
+
"networkDataSaver.interface.wifi": "Wi-Fi",
|
|
1555
|
+
"networkDataSaver.interface.wired-ethernet": "유선 이더넷",
|
|
1556
|
+
"networkDataSaver.interface.cellular": "셀룰러",
|
|
1557
|
+
"networkDataSaver.interface.loopback": "로컬 연결",
|
|
1558
|
+
"networkDataSaver.interface.other": "기타 연결",
|
|
1559
|
+
"networkDataSaver.interface.unknown": "알 수 없음",
|
|
1560
|
+
"networkDataSaver.reasonLabel": "판단 이유",
|
|
1561
|
+
"networkDataSaver.reason.expensive": "비용이 드는 네트워크로 감지됨",
|
|
1562
|
+
"networkDataSaver.reason.constrained": "저데이터 모드로 감지됨",
|
|
1563
|
+
"networkDataSaver.reason.offline": "오프라인",
|
|
1564
|
+
"networkDataSaver.reason.unrestricted": "일반 네트워크로 감지됨",
|
|
1565
|
+
"networkDataSaver.reason.unknown": "네트워크 비용 상태를 알 수 없음",
|
|
1566
|
+
"networkDataSaver.reason.user-always": "사용자가 항상 절약을 선택함",
|
|
1567
|
+
"networkDataSaver.reason.user-never": "사용자가 절약 안 함을 선택함",
|
|
1568
|
+
"networkDataSaver.deferred.title": "절약 중 지연되는 작업",
|
|
1569
|
+
"networkDataSaver.activeHelp": "수동 새로고침은 계속 사용할 수 있고, 대용량 작업은 확인 후 한 번 허용할 수 있습니다.",
|
|
1570
|
+
"networkDataSaver.deferred.model": "난이도 모델 최초 다운로드",
|
|
1571
|
+
"networkDataSaver.deferred.memory": "Memory Layer 대화 백필",
|
|
1572
|
+
"networkDataSaver.deferred.gitPr": "백그라운드 Git 및 PR 새로고침",
|
|
1503
1573
|
"happyCliUpdate.title": "Happy CLI 업데이트가 있습니다",
|
|
1504
1574
|
"happyCliUpdate.checkTitle": "Happy CLI 업데이트 확인",
|
|
1505
1575
|
"happyCliUpdate.managerTitle": "Happy CLI 업데이트 관리",
|
|
@@ -3397,6 +3467,7 @@ const ko = {
|
|
|
3397
3467
|
"file.readFailed": "파일을 읽지 못했습니다.",
|
|
3398
3468
|
"session.loadMessagesFailed": "대화 메시지를 불러올 수 없습니다.",
|
|
3399
3469
|
"session.sendToSessionFailed": "대화에 메시지를 보낼 수 없습니다.",
|
|
3470
|
+
"session.sendBlockedAccountKeyLocked": "이 대화는 계정 키로 잠겨 있습니다. 잠금을 해제한 뒤 다시 시도하세요.",
|
|
3400
3471
|
"message.noEncryptKey": "메시지 삭제 이벤트를 암호화할 세션 키를 찾을 수 없습니다.",
|
|
3401
3472
|
"machineSel.boundMissing": "프로젝트에 연결된 머신 정보를 찾을 수 없습니다. 새로고침 후 다시 시도해주세요.",
|
|
3402
3473
|
"machineSel.connectedMachine": "연결된 머신",
|
|
@@ -3784,6 +3855,7 @@ const ko = {
|
|
|
3784
3855
|
"settingsTab.language": "언어",
|
|
3785
3856
|
"settingsTab.machines": "머신",
|
|
3786
3857
|
"settingsTab.mobile": "모바일 연결",
|
|
3858
|
+
"settingsTab.network": "네트워크 데이터 절약",
|
|
3787
3859
|
"settingsTab.env": "환경 변수",
|
|
3788
3860
|
"settingsTab.security": "보안",
|
|
3789
3861
|
"settingsTab.memory": "Memory",
|
|
@@ -3792,12 +3864,15 @@ const ko = {
|
|
|
3792
3864
|
"settingsTab.extensions": "확장",
|
|
3793
3865
|
"settingsTab.about": "정보",
|
|
3794
3866
|
"settingsTab.shortcuts": "단축키",
|
|
3795
|
-
"extensions.help": "필요한 기능만 로컬 확장 패키지로 설치하고 관리합니다.",
|
|
3867
|
+
"extensions.help": "필요한 기능만 공식 또는 로컬 확장 패키지로 설치하고 관리합니다.",
|
|
3796
3868
|
"extensions.installLocal": "로컬 파일 설치",
|
|
3797
3869
|
"extensions.loading": "설치된 확장을 불러오는 중입니다.",
|
|
3798
3870
|
"extensions.empty": "설치된 확장이 없습니다.",
|
|
3799
3871
|
"extensions.permissions": "권한",
|
|
3800
3872
|
"extensions.noPermissions": "요청 권한 없음",
|
|
3873
|
+
"extensions.permission.browserViewerOpen": "접근 가능한 온라인 머신에서 원격 Chrome 화면을 준비해 기본 브라우저로 엽니다. 소프트웨어 설치는 포함하지 않습니다.",
|
|
3874
|
+
"extensions.permission.browserViewerInstall": "선택한 머신에 원격 화면용 시스템 패키지(Xvfb/noVNC)를 설치합니다. Chrome 설치는 포함하지 않습니다.",
|
|
3875
|
+
"extensions.permission.browserViewerInstallChrome": "선택한 머신에 Google Chrome을 설치합니다. 머신의 시스템 소프트웨어가 변경됩니다.",
|
|
3801
3876
|
"extensions.approvePermission": "'{permission}' 승인",
|
|
3802
3877
|
"extensions.revokePermission": "'{permission}' 철회",
|
|
3803
3878
|
"extensions.enable": "활성화",
|
|
@@ -6904,6 +6979,147 @@ function decryptJsonWithCandidates(blob, keys) {
|
|
|
6904
6979
|
decryptCacheSet(blob, keys, null);
|
|
6905
6980
|
return null;
|
|
6906
6981
|
}
|
|
6982
|
+
function bounded(value2, maxLength) {
|
|
6983
|
+
if (typeof value2 !== "string" || value2.length === 0 || value2.length > maxLength) return void 0;
|
|
6984
|
+
return value2;
|
|
6985
|
+
}
|
|
6986
|
+
function parseCloudLoginResult(raw) {
|
|
6987
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
6988
|
+
throw new Error("CLOUD_LOGIN_RESPONSE_INVALID");
|
|
6989
|
+
}
|
|
6990
|
+
const value2 = raw;
|
|
6991
|
+
const required = (field, maxLength) => {
|
|
6992
|
+
const entry = bounded(value2[field], maxLength);
|
|
6993
|
+
if (entry === void 0) throw new Error("CLOUD_LOGIN_RESPONSE_INVALID");
|
|
6994
|
+
return entry;
|
|
6995
|
+
};
|
|
6996
|
+
const accountPublicKey = bounded(value2.accountPublicKey, 128);
|
|
6997
|
+
const accountKeySalt = bounded(value2.accountKeySalt, 128);
|
|
6998
|
+
return {
|
|
6999
|
+
userId: required("userId", 256),
|
|
7000
|
+
username: required("username", 256),
|
|
7001
|
+
token: required("token", 8192),
|
|
7002
|
+
secret: required("secret", 8192),
|
|
7003
|
+
...accountPublicKey ? { accountPublicKey } : {},
|
|
7004
|
+
...accountKeySalt ? { accountKeySalt } : {}
|
|
7005
|
+
};
|
|
7006
|
+
}
|
|
7007
|
+
const ENVELOPE_VERSION = 0;
|
|
7008
|
+
const EPHEMERAL_PUBLIC_KEY_LENGTH = 32;
|
|
7009
|
+
const ENVELOPE_NONCE_LENGTH = 24;
|
|
7010
|
+
const ENVELOPE_HEADER_LENGTH = 1 + EPHEMERAL_PUBLIC_KEY_LENGTH + ENVELOPE_NONCE_LENGTH;
|
|
7011
|
+
const GCM_VERSION = 0;
|
|
7012
|
+
const GCM_NONCE_LENGTH = 12;
|
|
7013
|
+
const GCM_TAG_LENGTH = 16;
|
|
7014
|
+
function unwrapDataEncryptionKey(envelopeBase64, recipientPrivateKey) {
|
|
7015
|
+
let envelope;
|
|
7016
|
+
try {
|
|
7017
|
+
envelope = decodeBase64(envelopeBase64);
|
|
7018
|
+
} catch {
|
|
7019
|
+
return null;
|
|
7020
|
+
}
|
|
7021
|
+
if (envelope.length <= ENVELOPE_HEADER_LENGTH) return null;
|
|
7022
|
+
if (envelope[0] !== ENVELOPE_VERSION) return null;
|
|
7023
|
+
const ephemeralPublicKey = envelope.subarray(1, 1 + EPHEMERAL_PUBLIC_KEY_LENGTH);
|
|
7024
|
+
const nonce = envelope.subarray(1 + EPHEMERAL_PUBLIC_KEY_LENGTH, ENVELOPE_HEADER_LENGTH);
|
|
7025
|
+
const ciphertext = envelope.subarray(ENVELOPE_HEADER_LENGTH);
|
|
7026
|
+
const opened = nacl.box.open(ciphertext, nonce, ephemeralPublicKey, recipientPrivateKey);
|
|
7027
|
+
return opened ? new Uint8Array(opened) : null;
|
|
7028
|
+
}
|
|
7029
|
+
async function importGcmKey(dataEncryptionKey) {
|
|
7030
|
+
return crypto.subtle.importKey("raw", new Uint8Array(dataEncryptionKey), "AES-GCM", false, ["encrypt", "decrypt"]);
|
|
7031
|
+
}
|
|
7032
|
+
async function decryptDataKeyToString(dataEncryptionKey, bundleBase64) {
|
|
7033
|
+
let bundle;
|
|
7034
|
+
try {
|
|
7035
|
+
bundle = decodeBase64(bundleBase64);
|
|
7036
|
+
} catch {
|
|
7037
|
+
return null;
|
|
7038
|
+
}
|
|
7039
|
+
if (bundle.length < 1 + GCM_NONCE_LENGTH + GCM_TAG_LENGTH) return null;
|
|
7040
|
+
if (bundle[0] !== GCM_VERSION) return null;
|
|
7041
|
+
const nonce = bundle.subarray(1, 1 + GCM_NONCE_LENGTH);
|
|
7042
|
+
const ciphertextWithTag = bundle.subarray(1 + GCM_NONCE_LENGTH);
|
|
7043
|
+
try {
|
|
7044
|
+
const key = await importGcmKey(dataEncryptionKey);
|
|
7045
|
+
const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv: new Uint8Array(nonce) }, key, new Uint8Array(ciphertextWithTag));
|
|
7046
|
+
return new TextDecoder().decode(plaintext);
|
|
7047
|
+
} catch {
|
|
7048
|
+
return null;
|
|
7049
|
+
}
|
|
7050
|
+
}
|
|
7051
|
+
async function encryptDataKeyContent(dataEncryptionKey, value2) {
|
|
7052
|
+
const nonce = nacl.randomBytes(GCM_NONCE_LENGTH);
|
|
7053
|
+
const key = await importGcmKey(dataEncryptionKey);
|
|
7054
|
+
const plaintext = new TextEncoder().encode(JSON.stringify(value2));
|
|
7055
|
+
const ciphertextWithTag = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: new Uint8Array(nonce) }, key, plaintext));
|
|
7056
|
+
const bundle = new Uint8Array(1 + GCM_NONCE_LENGTH + ciphertextWithTag.length);
|
|
7057
|
+
bundle[0] = GCM_VERSION;
|
|
7058
|
+
bundle.set(nonce, 1);
|
|
7059
|
+
bundle.set(ciphertextWithTag, 1 + GCM_NONCE_LENGTH);
|
|
7060
|
+
return encodeBase64(bundle);
|
|
7061
|
+
}
|
|
7062
|
+
const dekBySessionId = /* @__PURE__ */ new Map();
|
|
7063
|
+
const lockedSessionIds = /* @__PURE__ */ new Set();
|
|
7064
|
+
const fingerprints = /* @__PURE__ */ new Map();
|
|
7065
|
+
function registerSessionDek(session, accountPrivateKey) {
|
|
7066
|
+
const envelope = session.dataEncryptionKey ?? null;
|
|
7067
|
+
if (!envelope) {
|
|
7068
|
+
dekBySessionId.delete(session.id);
|
|
7069
|
+
lockedSessionIds.delete(session.id);
|
|
7070
|
+
fingerprints.delete(session.id);
|
|
7071
|
+
return;
|
|
7072
|
+
}
|
|
7073
|
+
const fingerprint = `${envelope}|${accountPrivateKey ? "1" : "0"}`;
|
|
7074
|
+
if (fingerprints.get(session.id) === fingerprint) return;
|
|
7075
|
+
fingerprints.set(session.id, fingerprint);
|
|
7076
|
+
const dek = accountPrivateKey ? unwrapDataEncryptionKey(envelope, accountPrivateKey) : null;
|
|
7077
|
+
if (dek) {
|
|
7078
|
+
dekBySessionId.set(session.id, dek);
|
|
7079
|
+
lockedSessionIds.delete(session.id);
|
|
7080
|
+
} else {
|
|
7081
|
+
dekBySessionId.delete(session.id);
|
|
7082
|
+
lockedSessionIds.add(session.id);
|
|
7083
|
+
}
|
|
7084
|
+
}
|
|
7085
|
+
function getSessionDek(sessionId) {
|
|
7086
|
+
return dekBySessionId.get(sessionId) ?? null;
|
|
7087
|
+
}
|
|
7088
|
+
const MAX_ENTRIES = 3e3;
|
|
7089
|
+
const headerCache = /* @__PURE__ */ new Map();
|
|
7090
|
+
function cacheGet(store, sessionId, ciphertext) {
|
|
7091
|
+
return store.get(sessionId)?.get(ciphertext) ?? null;
|
|
7092
|
+
}
|
|
7093
|
+
function cacheSet(store, sessionId, ciphertext, plaintext) {
|
|
7094
|
+
let perSession = store.get(sessionId);
|
|
7095
|
+
if (!perSession) {
|
|
7096
|
+
perSession = /* @__PURE__ */ new Map();
|
|
7097
|
+
store.set(sessionId, perSession);
|
|
7098
|
+
}
|
|
7099
|
+
perSession.set(ciphertext, plaintext);
|
|
7100
|
+
if (perSession.size > MAX_ENTRIES) {
|
|
7101
|
+
const oldest = perSession.keys().next().value;
|
|
7102
|
+
if (oldest !== void 0) perSession.delete(oldest);
|
|
7103
|
+
}
|
|
7104
|
+
}
|
|
7105
|
+
function getCachedDataKeyMetadataJson(sessionId, ciphertext) {
|
|
7106
|
+
return cacheGet(headerCache, sessionId, ciphertext);
|
|
7107
|
+
}
|
|
7108
|
+
async function preDecryptSessionHeaders(sessions, accountPrivateKey) {
|
|
7109
|
+
await Promise.all(sessions.map(async (session) => {
|
|
7110
|
+
registerSessionDek(session, accountPrivateKey);
|
|
7111
|
+
const dek = getSessionDek(session.id);
|
|
7112
|
+
if (!dek) return;
|
|
7113
|
+
if (session.metadata && getCachedDataKeyMetadataJson(session.id, session.metadata) === null) {
|
|
7114
|
+
const json = await decryptDataKeyToString(dek, session.metadata);
|
|
7115
|
+
if (json !== null) cacheSet(headerCache, session.id, session.metadata, json);
|
|
7116
|
+
}
|
|
7117
|
+
if (session.agentState && getCachedDataKeyMetadataJson(session.id, session.agentState) === null) {
|
|
7118
|
+
const json = await decryptDataKeyToString(dek, session.agentState);
|
|
7119
|
+
if (json !== null) cacheSet(headerCache, session.id, session.agentState, json);
|
|
7120
|
+
}
|
|
7121
|
+
}));
|
|
7122
|
+
}
|
|
6907
7123
|
function buildUserTextMessageContent(input) {
|
|
6908
7124
|
return {
|
|
6909
7125
|
role: "user",
|
|
@@ -7022,7 +7238,7 @@ function personalMachinesPath(companyId) {
|
|
|
7022
7238
|
function myMachinesPath(companyId) {
|
|
7023
7239
|
return `/api/admin/companies/${encodeURIComponent(companyId)}/my-machines`;
|
|
7024
7240
|
}
|
|
7025
|
-
function record$
|
|
7241
|
+
function record$2(value2) {
|
|
7026
7242
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : {};
|
|
7027
7243
|
}
|
|
7028
7244
|
function array(value2) {
|
|
@@ -7041,7 +7257,7 @@ function provider(value2) {
|
|
|
7041
7257
|
return PROVIDERS.includes(str(value2)) ? str(value2) : null;
|
|
7042
7258
|
}
|
|
7043
7259
|
function parseBundle(value2) {
|
|
7044
|
-
const row2 = record$
|
|
7260
|
+
const row2 = record$2(value2);
|
|
7045
7261
|
const id = str(row2.id);
|
|
7046
7262
|
const bundleProvider = provider(row2.provider);
|
|
7047
7263
|
if (!id || !bundleProvider) return null;
|
|
@@ -7060,7 +7276,7 @@ function parseBundle(value2) {
|
|
|
7060
7276
|
};
|
|
7061
7277
|
}
|
|
7062
7278
|
function parseDeployment(value2) {
|
|
7063
|
-
const row2 = record$
|
|
7279
|
+
const row2 = record$2(value2);
|
|
7064
7280
|
const machineId = str(row2.machineId);
|
|
7065
7281
|
const bundleId = str(row2.bundleId);
|
|
7066
7282
|
if (!machineId || !bundleId) return null;
|
|
@@ -7080,10 +7296,10 @@ function parseDeployment(value2) {
|
|
|
7080
7296
|
};
|
|
7081
7297
|
}
|
|
7082
7298
|
function parseStatus(value2) {
|
|
7083
|
-
const row2 = record$
|
|
7299
|
+
const row2 = record$2(value2);
|
|
7084
7300
|
const statusProvider = provider(row2.provider);
|
|
7085
7301
|
if (!statusProvider) return void 0;
|
|
7086
|
-
const rotationRow = record$
|
|
7302
|
+
const rotationRow = record$2(row2.rotation);
|
|
7087
7303
|
const state = str(rotationRow.state);
|
|
7088
7304
|
const rotation = ROTATION_STATES.includes(state) ? {
|
|
7089
7305
|
state,
|
|
@@ -7104,7 +7320,7 @@ function parseStatus(value2) {
|
|
|
7104
7320
|
};
|
|
7105
7321
|
}
|
|
7106
7322
|
function parseMachineResult(value2) {
|
|
7107
|
-
const row2 = record$
|
|
7323
|
+
const row2 = record$2(value2);
|
|
7108
7324
|
const machineId = str(row2.machineId);
|
|
7109
7325
|
if (!machineId) return null;
|
|
7110
7326
|
if (row2.ok !== true) {
|
|
@@ -7119,28 +7335,28 @@ function parseMachineResult(value2) {
|
|
|
7119
7335
|
};
|
|
7120
7336
|
}
|
|
7121
7337
|
function parseAiCredentialVaultSnapshot(value2) {
|
|
7122
|
-
const body = record$
|
|
7338
|
+
const body = record$2(value2);
|
|
7123
7339
|
return {
|
|
7124
7340
|
bundles: array(body.bundles).map(parseBundle).filter((bundle) => bundle !== null),
|
|
7125
7341
|
deployments: array(body.deployments).map(parseDeployment).filter((deployment) => deployment !== null)
|
|
7126
7342
|
};
|
|
7127
7343
|
}
|
|
7128
7344
|
function parseAiCredentialCaptureResponse(value2) {
|
|
7129
|
-
const body = record$
|
|
7345
|
+
const body = record$2(value2);
|
|
7130
7346
|
const bundle = parseBundle(body.bundle);
|
|
7131
7347
|
if (!bundle) throw new Error("AI credential capture response did not contain a bundle");
|
|
7132
7348
|
return { bundle, warnings: array(body.warnings).filter((warning) => typeof warning === "string") };
|
|
7133
7349
|
}
|
|
7134
7350
|
function parseAiCredentialBatchResult(value2) {
|
|
7135
|
-
const body = record$
|
|
7351
|
+
const body = record$2(value2);
|
|
7136
7352
|
return {
|
|
7137
7353
|
results: array(body.results).map(parseMachineResult).filter((result) => result !== null),
|
|
7138
7354
|
warnings: array(body.warnings).filter((warning) => typeof warning === "string")
|
|
7139
7355
|
};
|
|
7140
7356
|
}
|
|
7141
7357
|
function parseSelfServiceMachines(value2) {
|
|
7142
|
-
return array(record$
|
|
7143
|
-
const row2 = record$
|
|
7358
|
+
return array(record$2(value2).machines).map((entry) => {
|
|
7359
|
+
const row2 = record$2(entry);
|
|
7144
7360
|
return {
|
|
7145
7361
|
machineId: str(row2.machineId),
|
|
7146
7362
|
type: row2.type === "shared" ? "shared" : "personal",
|
|
@@ -7153,8 +7369,8 @@ function parseSelfServiceMachines(value2) {
|
|
|
7153
7369
|
}).filter((machine) => machine.machineId !== "");
|
|
7154
7370
|
}
|
|
7155
7371
|
function parsePersonalMachines(value2) {
|
|
7156
|
-
return array(record$
|
|
7157
|
-
const row2 = record$
|
|
7372
|
+
return array(record$2(value2).machines).map((entry) => {
|
|
7373
|
+
const row2 = record$2(entry);
|
|
7158
7374
|
const type = row2.type === "shared" ? "shared" : "personal";
|
|
7159
7375
|
return {
|
|
7160
7376
|
machineId: str(row2.machineId),
|
|
@@ -7524,6 +7740,123 @@ function parseProjectListV2Page(body) {
|
|
|
7524
7740
|
total: page.total
|
|
7525
7741
|
};
|
|
7526
7742
|
}
|
|
7743
|
+
const PUBLIC_ID_PATTERN = /^[A-Za-z0-9_-]{32}$/;
|
|
7744
|
+
const CHECKSUM_PATTERN = /^[a-f\d]{64}$/i;
|
|
7745
|
+
const MAX_ID_LENGTH = 256;
|
|
7746
|
+
const MAX_TITLE_LENGTH = 255;
|
|
7747
|
+
const MAX_WARNINGS = 32;
|
|
7748
|
+
const MAX_WARNING_LENGTH = 500;
|
|
7749
|
+
const PUBLICATION_KEYS = /* @__PURE__ */ new Set([
|
|
7750
|
+
"id",
|
|
7751
|
+
"publicId",
|
|
7752
|
+
"sourceType",
|
|
7753
|
+
"sourceIdentity",
|
|
7754
|
+
"sourceChecksumSha256",
|
|
7755
|
+
"title",
|
|
7756
|
+
"contentType",
|
|
7757
|
+
"sizeBytes",
|
|
7758
|
+
"checksumSha256",
|
|
7759
|
+
"status",
|
|
7760
|
+
"createdAt",
|
|
7761
|
+
"revokedAt",
|
|
7762
|
+
"publicUrl"
|
|
7763
|
+
]);
|
|
7764
|
+
function fail(detail) {
|
|
7765
|
+
throw new Error(`artifact publication response is invalid: ${detail}`);
|
|
7766
|
+
}
|
|
7767
|
+
function record$1(value2, label) {
|
|
7768
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) fail(`${label} must be an object`);
|
|
7769
|
+
return value2;
|
|
7770
|
+
}
|
|
7771
|
+
function exactKeys(value2, keys) {
|
|
7772
|
+
const expected = new Set(keys);
|
|
7773
|
+
if (Object.keys(value2).some((key) => !expected.has(key)) || keys.some((key) => !(key in value2))) fail("response fields do not match the contract");
|
|
7774
|
+
}
|
|
7775
|
+
function nonEmptyString$1(value2, label, maxLength = MAX_ID_LENGTH) {
|
|
7776
|
+
if (typeof value2 !== "string" || !value2 || value2.trim() !== value2 || value2.length > maxLength) fail(`${label} is invalid`);
|
|
7777
|
+
return value2;
|
|
7778
|
+
}
|
|
7779
|
+
function checksum(value2, label) {
|
|
7780
|
+
if (typeof value2 !== "string" || !CHECKSUM_PATTERN.test(value2)) fail(`${label} is invalid`);
|
|
7781
|
+
}
|
|
7782
|
+
function safeInteger(value2, label) {
|
|
7783
|
+
if (typeof value2 !== "number" || !Number.isSafeInteger(value2) || value2 < 0) fail(`${label} is invalid`);
|
|
7784
|
+
}
|
|
7785
|
+
function warnings(value2) {
|
|
7786
|
+
if (!Array.isArray(value2) || value2.length > MAX_WARNINGS || value2.some((warning) => typeof warning !== "string" || warning.length > MAX_WARNING_LENGTH)) fail("warnings are invalid");
|
|
7787
|
+
return [...value2];
|
|
7788
|
+
}
|
|
7789
|
+
function isLoopbackHostname(hostname) {
|
|
7790
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
7791
|
+
}
|
|
7792
|
+
function parseTrustedPublicUrl(value2, apiBaseUrl, publicId) {
|
|
7793
|
+
if (typeof value2 !== "string") fail("publicUrl is invalid");
|
|
7794
|
+
let publicUrl;
|
|
7795
|
+
let apiUrl;
|
|
7796
|
+
try {
|
|
7797
|
+
publicUrl = new URL(value2);
|
|
7798
|
+
apiUrl = new URL(apiBaseUrl);
|
|
7799
|
+
} catch {
|
|
7800
|
+
fail("publicUrl is invalid");
|
|
7801
|
+
}
|
|
7802
|
+
if (publicUrl.protocol !== "https:" && !(publicUrl.protocol === "http:" && isLoopbackHostname(publicUrl.hostname)) || publicUrl.origin !== apiUrl.origin || publicUrl.protocol !== apiUrl.protocol || publicUrl.username || publicUrl.password || publicUrl.search || publicUrl.hash || publicUrl.pathname !== `/artifacts/${publicId}`) fail("publicUrl is not trusted");
|
|
7803
|
+
return publicUrl.toString();
|
|
7804
|
+
}
|
|
7805
|
+
function sourceIdentity(source) {
|
|
7806
|
+
return source.type === "project-file" ? JSON.stringify([
|
|
7807
|
+
source.projectId,
|
|
7808
|
+
...source.worktreeId ? [source.worktreeId] : [],
|
|
7809
|
+
source.path
|
|
7810
|
+
]) : JSON.stringify([source.chatId, source.path]);
|
|
7811
|
+
}
|
|
7812
|
+
function validatePublication(value2, apiBaseUrl, operation, expectedSource) {
|
|
7813
|
+
const publication = record$1(value2, "publication");
|
|
7814
|
+
if (Object.keys(publication).some((key) => !PUBLICATION_KEYS.has(key))) {
|
|
7815
|
+
fail("publication contains an unexpected field");
|
|
7816
|
+
}
|
|
7817
|
+
if ([...PUBLICATION_KEYS].some((key) => !(key in publication))) {
|
|
7818
|
+
fail("publication is missing a field");
|
|
7819
|
+
}
|
|
7820
|
+
const publicationId = nonEmptyString$1(publication.id, "publication id");
|
|
7821
|
+
const publicId = nonEmptyString$1(publication.publicId, "public id");
|
|
7822
|
+
if (!PUBLIC_ID_PATTERN.test(publicId)) fail("public id is invalid");
|
|
7823
|
+
if (!["project-file", "personal-chat-file", "personal-chat-artifact"].includes(String(publication.sourceType))) {
|
|
7824
|
+
fail("source type is invalid");
|
|
7825
|
+
}
|
|
7826
|
+
const parsedSourceIdentity = nonEmptyString$1(publication.sourceIdentity, "source identity", 4096);
|
|
7827
|
+
if (expectedSource && (publication.sourceType !== expectedSource.type || parsedSourceIdentity !== sourceIdentity(expectedSource))) fail("source does not match the request");
|
|
7828
|
+
checksum(publication.sourceChecksumSha256, "source checksum");
|
|
7829
|
+
const title = nonEmptyString$1(publication.title, "title", MAX_TITLE_LENGTH);
|
|
7830
|
+
nonEmptyString$1(publication.contentType, "content type");
|
|
7831
|
+
safeInteger(publication.sizeBytes, "size");
|
|
7832
|
+
checksum(publication.checksumSha256, "checksum");
|
|
7833
|
+
safeInteger(publication.createdAt, "created time");
|
|
7834
|
+
const expectedStatus = operation === "publish" ? "active" : "revoked";
|
|
7835
|
+
if (publication.status !== expectedStatus) fail("status does not match the operation");
|
|
7836
|
+
if (publication.revokedAt !== null && typeof publication.revokedAt !== "number" || typeof publication.revokedAt === "number" && (!Number.isSafeInteger(publication.revokedAt) || publication.revokedAt < 0) || expectedStatus === "active" && publication.revokedAt !== null || expectedStatus === "revoked" && publication.revokedAt === null) fail("revoked time is invalid");
|
|
7837
|
+
return {
|
|
7838
|
+
publicationId,
|
|
7839
|
+
publicId,
|
|
7840
|
+
publicUrl: parseTrustedPublicUrl(publication.publicUrl, apiBaseUrl, publicId),
|
|
7841
|
+
title,
|
|
7842
|
+
status: expectedStatus
|
|
7843
|
+
};
|
|
7844
|
+
}
|
|
7845
|
+
function parseStaticArtifactPublicationResult(value2, options) {
|
|
7846
|
+
const response = record$1(value2, "response");
|
|
7847
|
+
if (options.operation === "publish") {
|
|
7848
|
+
exactKeys(response, ["publication", "publicUrl", "reused"]);
|
|
7849
|
+
if (typeof response.reused !== "boolean") fail("reused is invalid");
|
|
7850
|
+
const publication2 = validatePublication(response.publication, options.apiBaseUrl, options.operation, options.source);
|
|
7851
|
+
const publicUrl = parseTrustedPublicUrl(response.publicUrl, options.apiBaseUrl, publication2.publicId);
|
|
7852
|
+
if (publication2.publicUrl !== publicUrl) fail("public URLs do not match");
|
|
7853
|
+
return { version: 1, ...publication2, reused: response.reused, warnings: [] };
|
|
7854
|
+
}
|
|
7855
|
+
exactKeys(response, ["publication", "warnings"]);
|
|
7856
|
+
const parsedWarnings = warnings(response.warnings);
|
|
7857
|
+
const publication = validatePublication(response.publication, options.apiBaseUrl, options.operation);
|
|
7858
|
+
return { version: 1, ...publication, reused: false, warnings: parsedWarnings };
|
|
7859
|
+
}
|
|
7527
7860
|
function asRecord(value2) {
|
|
7528
7861
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
7529
7862
|
}
|
|
@@ -16132,6 +16465,18 @@ function coerceCreatedBy(value2) {
|
|
|
16132
16465
|
const displayName = nonEmptyString(value2.displayName);
|
|
16133
16466
|
return displayName ? { accountId, displayName } : { accountId };
|
|
16134
16467
|
}
|
|
16468
|
+
function decryptSessionJsonSync(sessionId, blob, candidateKeys) {
|
|
16469
|
+
if (!blob) return null;
|
|
16470
|
+
const cached = getCachedDataKeyMetadataJson(sessionId, blob);
|
|
16471
|
+
if (cached !== null) {
|
|
16472
|
+
try {
|
|
16473
|
+
return JSON.parse(cached);
|
|
16474
|
+
} catch {
|
|
16475
|
+
return null;
|
|
16476
|
+
}
|
|
16477
|
+
}
|
|
16478
|
+
return decryptJsonWithCandidates(blob, candidateKeys);
|
|
16479
|
+
}
|
|
16135
16480
|
function apiSessionToSession(api, candidateKeys, existing) {
|
|
16136
16481
|
const updatedAt = mergeUpdatedAt(api.updatedAt);
|
|
16137
16482
|
const summaryText = decryptSummaryText(api.metadata, candidateKeys) ?? existing?.summaryText;
|
|
@@ -16141,8 +16486,8 @@ function apiSessionToSession(api, candidateKeys, existing) {
|
|
|
16141
16486
|
const pendingExitPlanMode = apiSessionAgentStatePendingExitPlanMode(api.agentState, candidateKeys);
|
|
16142
16487
|
const pendingPermissionRequests = apiSessionAgentStatePendingPermissions(api.agentState, candidateKeys);
|
|
16143
16488
|
const agentControlledByUser = apiSessionAgentStateControlledByUser(api.agentState, candidateKeys);
|
|
16144
|
-
const agentState = api.agentState ?
|
|
16145
|
-
const metadata =
|
|
16489
|
+
const agentState = api.agentState ? decryptSessionJsonSync(api.id, api.agentState, candidateKeys) : null;
|
|
16490
|
+
const metadata = decryptSessionJsonSync(api.id, api.metadata, candidateKeys);
|
|
16146
16491
|
const agent = agentFromFlavor(typeof metadata?.flavor === "string" ? metadata.flavor : void 0) ?? existing?.agent;
|
|
16147
16492
|
const models = coerceSessionModels(metadata?.models) ?? existing?.models;
|
|
16148
16493
|
const worktree = coerceSessionWorktree(metadata?.worktree) ?? coerceSessionWorktree(metadata) ?? existing?.worktree;
|
|
@@ -16486,18 +16831,18 @@ function readCloudMobileLoginPairingErrorCode(body) {
|
|
|
16486
16831
|
const PAIRING_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
16487
16832
|
const CODE_PATTERN = /^[A-Za-z0-9_-]{43}$/;
|
|
16488
16833
|
const STATUSES = /* @__PURE__ */ new Set(["pending", "consumed", "revoked", "expired"]);
|
|
16489
|
-
function invalidResponse() {
|
|
16834
|
+
function invalidResponse$1() {
|
|
16490
16835
|
throw new Error("PAIRING_RESPONSE_INVALID");
|
|
16491
16836
|
}
|
|
16492
16837
|
function parseCloudMobileLoginPairing(raw, options = {}) {
|
|
16493
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) invalidResponse();
|
|
16838
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) invalidResponse$1();
|
|
16494
16839
|
const value2 = raw;
|
|
16495
|
-
if (typeof value2.pairingId !== "string" || !PAIRING_ID_PATTERN.test(value2.pairingId)) invalidResponse();
|
|
16496
|
-
if (!Number.isSafeInteger(value2.expiresAt) || value2.expiresAt <= 0) invalidResponse();
|
|
16497
|
-
if (!Number.isSafeInteger(value2.serverTime) || value2.serverTime <= 0) invalidResponse();
|
|
16498
|
-
if (typeof value2.status !== "string" || !STATUSES.has(value2.status)) invalidResponse();
|
|
16499
|
-
if (options.requireCode && (typeof value2.code !== "string" || !CODE_PATTERN.test(value2.code))) invalidResponse();
|
|
16500
|
-
if (value2.code !== void 0 && (typeof value2.code !== "string" || !CODE_PATTERN.test(value2.code))) invalidResponse();
|
|
16840
|
+
if (typeof value2.pairingId !== "string" || !PAIRING_ID_PATTERN.test(value2.pairingId)) invalidResponse$1();
|
|
16841
|
+
if (!Number.isSafeInteger(value2.expiresAt) || value2.expiresAt <= 0) invalidResponse$1();
|
|
16842
|
+
if (!Number.isSafeInteger(value2.serverTime) || value2.serverTime <= 0) invalidResponse$1();
|
|
16843
|
+
if (typeof value2.status !== "string" || !STATUSES.has(value2.status)) invalidResponse$1();
|
|
16844
|
+
if (options.requireCode && (typeof value2.code !== "string" || !CODE_PATTERN.test(value2.code))) invalidResponse$1();
|
|
16845
|
+
if (value2.code !== void 0 && (typeof value2.code !== "string" || !CODE_PATTERN.test(value2.code))) invalidResponse$1();
|
|
16501
16846
|
return {
|
|
16502
16847
|
pairingId: value2.pairingId,
|
|
16503
16848
|
expiresAt: value2.expiresAt,
|
|
@@ -16506,6 +16851,43 @@ function parseCloudMobileLoginPairing(raw, options = {}) {
|
|
|
16506
16851
|
...typeof value2.code === "string" ? { code: value2.code } : {}
|
|
16507
16852
|
};
|
|
16508
16853
|
}
|
|
16854
|
+
const BROWSER_VIEWER_OPEN_TIMEOUT_MS = 13e4;
|
|
16855
|
+
const BROWSER_VIEWER_INSTALL_TIMEOUT_MS = 31e4;
|
|
16856
|
+
function invalidResponse() {
|
|
16857
|
+
throw new Error("BROWSER_VIEWER_RESPONSE_INVALID");
|
|
16858
|
+
}
|
|
16859
|
+
function responseRecord(value2) {
|
|
16860
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) invalidResponse();
|
|
16861
|
+
return value2;
|
|
16862
|
+
}
|
|
16863
|
+
function parseBrowserViewerOpenResponse(value2) {
|
|
16864
|
+
const response = responseRecord(value2);
|
|
16865
|
+
if (typeof response.token !== "string" || response.token.length === 0 || typeof response.relayUrl !== "string" && response.relayUrl !== null || response.routingVersion !== 1 || typeof response.previewDomain !== "string" && response.previewDomain !== null || !Number.isInteger(response.webPort) || response.webPort < 1 || response.webPort > 65535 || typeof response.bridgeReady !== "boolean") invalidResponse();
|
|
16866
|
+
return {
|
|
16867
|
+
token: response.token,
|
|
16868
|
+
relayUrl: response.relayUrl,
|
|
16869
|
+
routingVersion: 1,
|
|
16870
|
+
previewDomain: response.previewDomain,
|
|
16871
|
+
webPort: response.webPort,
|
|
16872
|
+
bridgeReady: response.bridgeReady
|
|
16873
|
+
};
|
|
16874
|
+
}
|
|
16875
|
+
function parseBrowserViewerInstallResponse(value2) {
|
|
16876
|
+
const response = responseRecord(value2);
|
|
16877
|
+
if (response.action === "already-installed") return { kind: "already-installed" };
|
|
16878
|
+
if (response.action === "run") {
|
|
16879
|
+
if (response.ok !== true) invalidResponse();
|
|
16880
|
+
return { kind: "installed" };
|
|
16881
|
+
}
|
|
16882
|
+
if (response.action === "manual") {
|
|
16883
|
+
if (typeof response.command !== "string" || typeof response.reason !== "string") invalidResponse();
|
|
16884
|
+
return {
|
|
16885
|
+
kind: "manual-required",
|
|
16886
|
+
reason: response.command.length === 0 ? "unsupported-platform" : "admin-required"
|
|
16887
|
+
};
|
|
16888
|
+
}
|
|
16889
|
+
return invalidResponse();
|
|
16890
|
+
}
|
|
16509
16891
|
const FIXED_API_BASE_URL = "https://saycode.ai";
|
|
16510
16892
|
const FIXED_SYNC_BASE_URL = "https://saycode.ai";
|
|
16511
16893
|
const machineRegistryCache = /* @__PURE__ */ new Map();
|
|
@@ -16686,6 +17068,7 @@ function normalizeMachine(row2, candidateKeys) {
|
|
|
16686
17068
|
mcpCallerGrantPublicKey: readMcpCallerGrantPublicKey(daemonState),
|
|
16687
17069
|
automationSupport,
|
|
16688
17070
|
filesystemScope: decrypted.filesystemScope,
|
|
17071
|
+
additionalDirectories: decrypted.additionalDirectories,
|
|
16689
17072
|
registry
|
|
16690
17073
|
};
|
|
16691
17074
|
}
|
|
@@ -16713,7 +17096,7 @@ class ApiClient {
|
|
|
16713
17096
|
headers: { "Content-Type": "application/json" },
|
|
16714
17097
|
body: JSON.stringify(input)
|
|
16715
17098
|
}, DEFAULT_REQUEST_TIMEOUT_MS);
|
|
16716
|
-
return this.handleResponse(response);
|
|
17099
|
+
return parseCloudLoginResult(await this.handleResponse(response));
|
|
16717
17100
|
}
|
|
16718
17101
|
async getMe() {
|
|
16719
17102
|
return this.request("/api/auth/me", { boundary: "api" });
|
|
@@ -17952,23 +18335,26 @@ class ApiClient {
|
|
|
17952
18335
|
boundary: "api"
|
|
17953
18336
|
});
|
|
17954
18337
|
}
|
|
17955
|
-
async fetchSessions(candidateKeys = [], tokenOverride) {
|
|
18338
|
+
async fetchSessions(candidateKeys = [], tokenOverride, accountPrivateKey = null) {
|
|
17956
18339
|
const data = await this.request("/v1/sessions", {
|
|
17957
18340
|
boundary: "sync",
|
|
17958
18341
|
tokenOverride
|
|
17959
18342
|
});
|
|
17960
18343
|
const rows = Array.isArray(data) ? data : data.sessions ?? [];
|
|
18344
|
+
await preDecryptSessionHeaders(rows, accountPrivateKey);
|
|
17961
18345
|
return rows.map((row2) => apiSessionToSession(row2, candidateKeys));
|
|
17962
18346
|
}
|
|
17963
|
-
async fetchSessionsPage(candidateKeys = [], cursor, limit = 200, tokenOverride) {
|
|
18347
|
+
async fetchSessionsPage(candidateKeys = [], cursor, limit = 200, tokenOverride, accountPrivateKey = null) {
|
|
17964
18348
|
const params = new URLSearchParams({ limit: String(limit) });
|
|
17965
18349
|
if (cursor) params.set("cursor", cursor);
|
|
17966
18350
|
const data = await this.request(
|
|
17967
18351
|
`/v2/sessions?${params.toString()}`,
|
|
17968
18352
|
{ boundary: "sync", tokenOverride }
|
|
17969
18353
|
);
|
|
18354
|
+
const rows = data.sessions ?? [];
|
|
18355
|
+
await preDecryptSessionHeaders(rows, accountPrivateKey);
|
|
17970
18356
|
return {
|
|
17971
|
-
sessions:
|
|
18357
|
+
sessions: rows.map((row2) => apiSessionToSession(row2, candidateKeys)),
|
|
17972
18358
|
nextCursor: data.nextCursor ?? null,
|
|
17973
18359
|
hasNext: data.hasNext === true
|
|
17974
18360
|
};
|
|
@@ -17983,8 +18369,10 @@ class ApiClient {
|
|
|
17983
18369
|
});
|
|
17984
18370
|
return data.sessions ?? [];
|
|
17985
18371
|
}
|
|
17986
|
-
async lookupSessions(candidateKeys, ids, tokenOverride) {
|
|
17987
|
-
|
|
18372
|
+
async lookupSessions(candidateKeys, ids, tokenOverride, accountPrivateKey = null) {
|
|
18373
|
+
const rows = await this.lookupApiSessions(ids, tokenOverride);
|
|
18374
|
+
await preDecryptSessionHeaders(rows, accountPrivateKey);
|
|
18375
|
+
return rows.map((row2) => apiSessionToSession(row2, candidateKeys));
|
|
17988
18376
|
}
|
|
17989
18377
|
async deleteSession(sessionId, tokenOverride) {
|
|
17990
18378
|
await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
|
|
@@ -18169,9 +18557,8 @@ class ApiClient {
|
|
|
18169
18557
|
}
|
|
18170
18558
|
async sendUserTextMessage(input) {
|
|
18171
18559
|
const localId = input.localId || `desktop-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
18172
|
-
const key = input.secretBase64 ? decodeBase64(input.secretBase64) : null;
|
|
18173
18560
|
const content = buildUserTextMessageContent(input);
|
|
18174
|
-
const encoded = encodeMessageContent(content,
|
|
18561
|
+
const encoded = input.dataEncryptionKey ? await encryptDataKeyContent(input.dataEncryptionKey, content) : encodeMessageContent(content, input.secretBase64 ? decodeBase64(input.secretBase64) : null);
|
|
18175
18562
|
const result = await this.sendMessages(input.sessionId, [{ localId, content: encoded }], input.tokenOverride);
|
|
18176
18563
|
return { localId, sentSeq: result.messages?.[0]?.seq };
|
|
18177
18564
|
}
|
|
@@ -18243,6 +18630,34 @@ class ApiClient {
|
|
|
18243
18630
|
}
|
|
18244
18631
|
return resolvedProjectPreviewRelayUrl({ ...input, response: { ...response, token: response.token }, syncBaseUrl: this.config.syncBaseUrl });
|
|
18245
18632
|
}
|
|
18633
|
+
async openBrowserViewer(machineId, signal) {
|
|
18634
|
+
return parseBrowserViewerOpenResponse(await this.request(
|
|
18635
|
+
`/api/machines/${encodeURIComponent(machineId)}/browser-viewer/open`,
|
|
18636
|
+
{
|
|
18637
|
+
method: "POST",
|
|
18638
|
+
boundary: "api",
|
|
18639
|
+
signal,
|
|
18640
|
+
timeoutMs: BROWSER_VIEWER_OPEN_TIMEOUT_MS
|
|
18641
|
+
}
|
|
18642
|
+
));
|
|
18643
|
+
}
|
|
18644
|
+
async installBrowserViewer(machineId, signal) {
|
|
18645
|
+
return this.installBrowserViewerComponent(machineId, "install", signal);
|
|
18646
|
+
}
|
|
18647
|
+
async installBrowserViewerChrome(machineId, signal) {
|
|
18648
|
+
return this.installBrowserViewerComponent(machineId, "install-chrome", signal);
|
|
18649
|
+
}
|
|
18650
|
+
async installBrowserViewerComponent(machineId, action, signal) {
|
|
18651
|
+
return parseBrowserViewerInstallResponse(await this.request(
|
|
18652
|
+
`/api/machines/${encodeURIComponent(machineId)}/browser-viewer/${action}`,
|
|
18653
|
+
{
|
|
18654
|
+
method: "POST",
|
|
18655
|
+
boundary: "api",
|
|
18656
|
+
signal,
|
|
18657
|
+
timeoutMs: BROWSER_VIEWER_INSTALL_TIMEOUT_MS
|
|
18658
|
+
}
|
|
18659
|
+
));
|
|
18660
|
+
}
|
|
18246
18661
|
// --- Project deploy & share (specs/desktop-project-deploy-share) ---
|
|
18247
18662
|
async moveProject(projectId, input) {
|
|
18248
18663
|
return this.request(`/api/projects/${encodeURIComponent(projectId)}/move`, {
|
|
@@ -18364,6 +18779,30 @@ class ApiClient {
|
|
|
18364
18779
|
timeoutMs: LONG_REQUEST_TIMEOUT_MS
|
|
18365
18780
|
});
|
|
18366
18781
|
}
|
|
18782
|
+
async publishStaticArtifact(source, signal) {
|
|
18783
|
+
const { version: _version, ...serverSource } = source;
|
|
18784
|
+
const response = await this.request("/api/artifact-publications", {
|
|
18785
|
+
method: "POST",
|
|
18786
|
+
boundary: "api",
|
|
18787
|
+
body: JSON.stringify({ source: serverSource }),
|
|
18788
|
+
signal
|
|
18789
|
+
});
|
|
18790
|
+
return parseStaticArtifactPublicationResult(response, {
|
|
18791
|
+
apiBaseUrl: this.config.apiBaseUrl,
|
|
18792
|
+
operation: "publish",
|
|
18793
|
+
source
|
|
18794
|
+
});
|
|
18795
|
+
}
|
|
18796
|
+
async revokeStaticArtifactPublication(publicationId, signal) {
|
|
18797
|
+
const response = await this.request(
|
|
18798
|
+
`/api/artifact-publications/${encodeURIComponent(publicationId)}`,
|
|
18799
|
+
{ method: "DELETE", boundary: "api", signal }
|
|
18800
|
+
);
|
|
18801
|
+
return parseStaticArtifactPublicationResult(response, {
|
|
18802
|
+
apiBaseUrl: this.config.apiBaseUrl,
|
|
18803
|
+
operation: "revoke"
|
|
18804
|
+
});
|
|
18805
|
+
}
|
|
18367
18806
|
async getDeployGitHubFiles(projectId, branch = "main") {
|
|
18368
18807
|
const data = await this.request("/deploy/github-files", {
|
|
18369
18808
|
method: "POST",
|
|
@@ -19784,11 +20223,11 @@ function reduceSessionReadState(state, change, merge, fingerprintLimit = DEFAULT
|
|
|
19784
20223
|
const { item } = change;
|
|
19785
20224
|
if (state.fingerprints.includes(item.fingerprint)) return state;
|
|
19786
20225
|
if (change.kind === "event" && item.cursor !== null && state.cursor !== null && item.cursor < state.cursor) return state;
|
|
19787
|
-
const
|
|
20226
|
+
const fingerprints2 = [...state.fingerprints, item.fingerprint].slice(-Math.max(1, fingerprintLimit));
|
|
19788
20227
|
return {
|
|
19789
20228
|
value: merge(state.value, item.value),
|
|
19790
20229
|
cursor: item.cursor === null ? state.cursor : Math.max(state.cursor ?? item.cursor, item.cursor),
|
|
19791
|
-
fingerprints
|
|
20230
|
+
fingerprints: fingerprints2
|
|
19792
20231
|
};
|
|
19793
20232
|
}
|
|
19794
20233
|
function record(value2) {
|
|
@@ -20741,7 +21180,7 @@ function chatUploadRelPath(file, id) {
|
|
|
20741
21180
|
if (file.type.startsWith("image/")) return `.aplus/uploads/${id}.${extensionFor(file)}`;
|
|
20742
21181
|
return `.aplus/uploads/${id}-${safeFileName(file)}`;
|
|
20743
21182
|
}
|
|
20744
|
-
async function uploadChatFileToMachineCore(file, workspaceRoot, deps) {
|
|
21183
|
+
async function uploadChatFileToMachineCore(file, workspaceRoot, deps, relativePath) {
|
|
20745
21184
|
const root = workspaceRoot.trim().replace(/\/+$/, "");
|
|
20746
21185
|
if (!root) throw new Error(translateCurrent("upload.noWorkspaceRoot"));
|
|
20747
21186
|
const id = (deps.now?.() ?? Date.now()).toString(36) + (deps.random?.() ?? Math.random()).toString(36).slice(2, 8);
|
|
@@ -21190,6 +21629,7 @@ const USAGE_SETTINGS_NAVIGATION_DESTINATIONS = [
|
|
|
21190
21629
|
"settings_shortcuts",
|
|
21191
21630
|
"settings_machines",
|
|
21192
21631
|
"settings_mobile",
|
|
21632
|
+
"settings_network",
|
|
21193
21633
|
"settings_env",
|
|
21194
21634
|
"settings_security",
|
|
21195
21635
|
"settings_memory",
|
|
@@ -24043,13 +24483,16 @@ function parseSpawnRecord(value2) {
|
|
|
24043
24483
|
}
|
|
24044
24484
|
const LEDGER_FILE_MODE = 384;
|
|
24045
24485
|
const PREVIEW_MAX_LENGTH = 200;
|
|
24046
|
-
function agentLineagePath(
|
|
24486
|
+
function agentLineagePath(rootDir) {
|
|
24487
|
+
return join(rootDir, ".aplus", "agent-lineage.jsonl");
|
|
24488
|
+
}
|
|
24489
|
+
function legacyAgentLineagePath(homeDir) {
|
|
24047
24490
|
return join(homeDir, ".saycode", "agent-lineage.jsonl");
|
|
24048
24491
|
}
|
|
24049
|
-
function
|
|
24492
|
+
function readLedger(path) {
|
|
24050
24493
|
let raw;
|
|
24051
24494
|
try {
|
|
24052
|
-
raw = readFileSync(
|
|
24495
|
+
raw = readFileSync(path, "utf8");
|
|
24053
24496
|
} catch (error) {
|
|
24054
24497
|
if (error.code === "ENOENT") return [];
|
|
24055
24498
|
throw error;
|
|
@@ -24067,12 +24510,38 @@ function readSpawnRecords(homeDir) {
|
|
|
24067
24510
|
}
|
|
24068
24511
|
return records;
|
|
24069
24512
|
}
|
|
24070
|
-
function
|
|
24071
|
-
return
|
|
24513
|
+
function recordIdentity(record2) {
|
|
24514
|
+
return JSON.stringify([
|
|
24515
|
+
record2.ts,
|
|
24516
|
+
record2.agentKey,
|
|
24517
|
+
record2.root,
|
|
24518
|
+
record2.depth,
|
|
24519
|
+
record2.childSessionId,
|
|
24520
|
+
record2.parentSessionId ?? null,
|
|
24521
|
+
record2.agent,
|
|
24522
|
+
record2.promptPreview,
|
|
24523
|
+
record2.outcome
|
|
24524
|
+
]);
|
|
24525
|
+
}
|
|
24526
|
+
function readSpawnRecords(homeDir, rootDir) {
|
|
24527
|
+
const records = [
|
|
24528
|
+
...readLedger(legacyAgentLineagePath(homeDir)),
|
|
24529
|
+
...readLedger(agentLineagePath(rootDir))
|
|
24530
|
+
];
|
|
24531
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24532
|
+
return records.filter((record2) => {
|
|
24533
|
+
const identity = recordIdentity(record2);
|
|
24534
|
+
if (seen.has(identity)) return false;
|
|
24535
|
+
seen.add(identity);
|
|
24536
|
+
return true;
|
|
24537
|
+
});
|
|
24538
|
+
}
|
|
24539
|
+
function countSpawnsFor(homeDir, rootDir, agentKey) {
|
|
24540
|
+
return readSpawnRecords(homeDir, rootDir).filter((record2) => record2.agentKey === agentKey).length;
|
|
24072
24541
|
}
|
|
24073
|
-
function appendSpawnRecord(
|
|
24074
|
-
const path = agentLineagePath(
|
|
24075
|
-
mkdirSync(join(
|
|
24542
|
+
function appendSpawnRecord(rootDir, record2) {
|
|
24543
|
+
const path = agentLineagePath(rootDir);
|
|
24544
|
+
mkdirSync(join(rootDir, ".aplus"), { recursive: true, mode: 448 });
|
|
24076
24545
|
const entry = { ...record2, promptPreview: record2.promptPreview.slice(0, PREVIEW_MAX_LENGTH) };
|
|
24077
24546
|
appendFileSync(path, `${JSON.stringify(entry)}
|
|
24078
24547
|
`, { mode: LEDGER_FILE_MODE });
|
|
@@ -24276,7 +24745,7 @@ async function openStream(context, transport, sessionId) {
|
|
|
24276
24745
|
}
|
|
24277
24746
|
function createAgentRuntime(context, transport, env = process.env) {
|
|
24278
24747
|
const keys = [decodeBase64(context.syncCredential.secret)];
|
|
24279
|
-
const homeDir = context.homeDir;
|
|
24748
|
+
const homeDir = context.homeDir, lineageRoot = readAgentEnv(env)?.root ?? context.homeDir;
|
|
24280
24749
|
const credentialHome = context.credential.source === "saycode" ? null : dirname(context.credential.path);
|
|
24281
24750
|
return {
|
|
24282
24751
|
env,
|
|
@@ -24317,9 +24786,9 @@ function createAgentRuntime(context, transport, env = process.env) {
|
|
|
24317
24786
|
return { sessionId: result.sessionId };
|
|
24318
24787
|
},
|
|
24319
24788
|
newAgentId: () => randomBytes(8).toString("hex"),
|
|
24320
|
-
countSpawns: (agentKey) => countSpawnsFor(homeDir, agentKey),
|
|
24321
|
-
recordSpawn: (record2) => appendSpawnRecord(
|
|
24322
|
-
readLineage: () => readSpawnRecords(homeDir),
|
|
24789
|
+
countSpawns: (agentKey) => countSpawnsFor(homeDir, lineageRoot, agentKey),
|
|
24790
|
+
recordSpawn: (record2) => appendSpawnRecord(lineageRoot, record2),
|
|
24791
|
+
readLineage: () => readSpawnRecords(homeDir, lineageRoot),
|
|
24323
24792
|
steerSession: (sessionId, text2) => steerAgentSession(transport, sessionId, text2),
|
|
24324
24793
|
stopSession: (machineId, sessionId) => stopAgentSession(transport, machineId, sessionId),
|
|
24325
24794
|
// Omitted entirely without API access: a Happy `access.key` is sync-only, so
|