@buzzni/saycode-cli 0.9.3 → 0.9.4
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 +1598 -474
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -49,7 +49,7 @@ function _mergeNamespaces(n, m) {
|
|
|
49
49
|
}
|
|
50
50
|
return Object.freeze(Object.defineProperty(n, Symbol.toStringTag, { value: "Module" }));
|
|
51
51
|
}
|
|
52
|
-
const version$3 = "0.9.
|
|
52
|
+
const version$3 = "0.9.4";
|
|
53
53
|
const manifest = {
|
|
54
54
|
version: version$3
|
|
55
55
|
};
|
|
@@ -831,6 +831,33 @@ const managedClaudeKo = {
|
|
|
831
831
|
"managedClaude.offline": "작업PC에 연결 중이에요.",
|
|
832
832
|
"managedClaude.loading": "AI 연결 중…"
|
|
833
833
|
};
|
|
834
|
+
const workTemplateKo = {
|
|
835
|
+
"work.gallery": "문서 템플릿",
|
|
836
|
+
"work.templates": "템플릿",
|
|
837
|
+
"work.create": "내 템플릿 만들기",
|
|
838
|
+
"work.closeGallery": "템플릿 목록 닫기",
|
|
839
|
+
"work.edit": "템플릿 편집",
|
|
840
|
+
"work.delete": "템플릿 삭제",
|
|
841
|
+
"work.deleteConfirm": "{name} 템플릿을 삭제할까요?",
|
|
842
|
+
"work.description": "기존 문서의 서식을 저장해 다음 Work에서 다시 사용할 수 있습니다.",
|
|
843
|
+
"work.name": "템플릿 이름",
|
|
844
|
+
"work.file": "원본 파일",
|
|
845
|
+
"work.chooseFile": "파일 선택",
|
|
846
|
+
"work.noFileSelected": "선택한 파일 없음",
|
|
847
|
+
"work.preserve": "유지할 서식과 작성 지침",
|
|
848
|
+
"work.save": "템플릿 저장",
|
|
849
|
+
"work.saving": "저장 중…",
|
|
850
|
+
"work.instructions": "작성 지침",
|
|
851
|
+
"work.saveChanges": "변경 저장",
|
|
852
|
+
"work.templateInvalid": "선택한 템플릿 원본을 확인할 수 없습니다.",
|
|
853
|
+
"work.templateChanged": "템플릿이 변경됐습니다. 다시 선택해 주세요.",
|
|
854
|
+
"work.templatesUnavailable": "템플릿 목록을 불러오지 못했습니다.",
|
|
855
|
+
"work.clearTemplate": "템플릿 선택 해제",
|
|
856
|
+
"work.templatesLoading": "템플릿 불러오는 중…",
|
|
857
|
+
"work.templateSaveFailed": "템플릿을 저장하지 못했습니다. 내용을 확인하고 다시 시도해 주세요.",
|
|
858
|
+
"work.templatePrompt": "{format} 문서를 {name} 템플릿 스킬로 작성해줘. 원본 양식의 디자인과 구조를 유지해줘.",
|
|
859
|
+
"work.taskCue": "내 작업은"
|
|
860
|
+
};
|
|
834
861
|
const workKo = {
|
|
835
862
|
"work.mediaChanged": "원본 파일이 변경됐습니다. 다시 열어주세요.",
|
|
836
863
|
"work.mediaUnsupported": "이 컴퓨터에서는 미디어 스트리밍을 지원하지 않습니다. 원본을 저장해 열어주세요.",
|
|
@@ -1034,6 +1061,7 @@ const organizationStartKo = {
|
|
|
1034
1061
|
"organizationStart.welcome": "조직에서 바로 시작해요",
|
|
1035
1062
|
"organizationStart.welcomeBody": "필요한 도구와 이 컴퓨터 연결을 자동으로 준비할게요.",
|
|
1036
1063
|
"organizationStart.chooseTitle": "어느 조직에서 작업할까요?",
|
|
1064
|
+
"organizationStart.resumeVerified": "확인된 조직으로 계속",
|
|
1037
1065
|
"organizationStart.next": "다음",
|
|
1038
1066
|
"organizationStart.switching": "조직 연결 중…",
|
|
1039
1067
|
"organizationStart.confirmConnectionTitle": "PC 연결 확인",
|
|
@@ -1186,11 +1214,30 @@ const organizationStartKo = {
|
|
|
1186
1214
|
"companyInvite.declineConfirm": "거절 확인",
|
|
1187
1215
|
"companyInvite.declineBody": "{name}의 초대를 거절할까요? 참여하려면 새 초대가 필요해요.",
|
|
1188
1216
|
"companyInvite.cancel": "취소",
|
|
1189
|
-
"companyInvite.loading": "초대 확인 중…",
|
|
1217
|
+
"companyInvite.loading": "조직과 초대 확인 중…",
|
|
1190
1218
|
"companyInvite.acceptLabel": "{name} 초대 수락",
|
|
1191
1219
|
"companyInvite.declineLabel": "{name} 초대 거절",
|
|
1192
1220
|
"companyInvite.declineConfirmLabel": "{name} 초대 거절 확인",
|
|
1193
|
-
"companyInvite.refreshFailed": "
|
|
1221
|
+
"companyInvite.refreshFailed": "조직과 초대를 확인하지 못했어요. 다시 확인해주세요.",
|
|
1222
|
+
"organizationEnrollment.join": "초대 코드로 참여",
|
|
1223
|
+
"organizationEnrollment.create": "조직 만들기",
|
|
1224
|
+
"organizationEnrollment.code": "초대 코드",
|
|
1225
|
+
"organizationEnrollment.name": "이름",
|
|
1226
|
+
"organizationEnrollment.joinSubmit": "조직에 참여",
|
|
1227
|
+
"organizationEnrollment.createSubmit": "만들기",
|
|
1228
|
+
"organizationEnrollment.back": "조직 참여 방법으로 돌아가기",
|
|
1229
|
+
"organizationEnrollment.joinHelp": "조직 관리자에게 받은 초대 코드를 입력해주세요.",
|
|
1230
|
+
"organizationEnrollment.working": "조직 정보를 확인 중…",
|
|
1231
|
+
"organizationEnrollment.joined": "조직이 확인됐어요. 다음을 눌러 시작 준비를 이어가세요.",
|
|
1232
|
+
"organizationEnrollment.unconfirmed": "처리 결과를 아직 확인하지 못했어요. 중복 요청 없이 조직 목록을 다시 확인한 뒤 참여한 조직을 선택해주세요.",
|
|
1233
|
+
"organizationEnrollment.recheck": "조직 목록 다시 확인",
|
|
1234
|
+
"organizationEnrollment.invalidCode": "초대 코드를 확인해주세요. 만료되거나 취소된 코드는 관리자에게 다시 요청해주세요.",
|
|
1235
|
+
"organizationEnrollment.slugTaken": "조직 주소를 준비하지 못했어요. 다시 시도해주세요.",
|
|
1236
|
+
"organizationEnrollment.rateLimited": "요청이 많아요. 잠시 후 다시 시도해주세요.",
|
|
1237
|
+
"organizationEnrollment.signInAgain": "로그인이 만료됐어요. 다시 로그인한 뒤 참여해주세요.",
|
|
1238
|
+
"organizationEnrollment.failed": "요청을 처리하지 못했어요. 입력 내용을 확인하고 다시 시도해주세요.",
|
|
1239
|
+
"organizationEnrollment.welcomeBody": "함께 작업할 조직을 선택하거나 새로 만들어주세요.",
|
|
1240
|
+
"organizationEnrollment.other": "다른 조직 선택·참여"
|
|
1194
1241
|
};
|
|
1195
1242
|
const usageEntryKo = {
|
|
1196
1243
|
...selfHostConnectKo,
|
|
@@ -1334,7 +1381,38 @@ const usageEntryKo = {
|
|
|
1334
1381
|
"onboarding.additionalStep": "추가 단계",
|
|
1335
1382
|
"onboarding.requiredSettings": "필수 설정",
|
|
1336
1383
|
"onboarding.additionalSettings": "추가 설정 — 선택",
|
|
1337
|
-
"onboarding.cli.bundledPreparation": "Saycode 도구는 앱에 포함되어 있어요. 필수 도구와 이 컴퓨터의 연결을 확인해요."
|
|
1384
|
+
"onboarding.cli.bundledPreparation": "Saycode 도구는 앱에 포함되어 있어요. 필수 도구와 이 컴퓨터의 연결을 확인해요.",
|
|
1385
|
+
"signup.open": "계정 만들기",
|
|
1386
|
+
"signup.title": "계정 만들기",
|
|
1387
|
+
"signup.sendCode": "인증 코드 받기",
|
|
1388
|
+
"signup.sendingCode": "코드 보내는 중…",
|
|
1389
|
+
"signup.code": "메일로 발송된 인증코드를 입력해주세요",
|
|
1390
|
+
"signup.resend": "다시 보내기",
|
|
1391
|
+
"signup.password": "비밀번호",
|
|
1392
|
+
"signup.confirmation": "비밀번호 확인",
|
|
1393
|
+
"signup.minimum": "12자 이상",
|
|
1394
|
+
"signup.mismatch": "비밀번호가 일치하지 않습니다.",
|
|
1395
|
+
"signup.submit": "가입하고 시작하기",
|
|
1396
|
+
"signup.creating": "계정 만드는 중…",
|
|
1397
|
+
"signup.back": "로그인",
|
|
1398
|
+
"signup.registered": "이미 가입된 이메일이에요. 로그인해 주세요.",
|
|
1399
|
+
"signup.rateLimited": "요청이 많아요. 잠시 후 다시 시도해 주세요.",
|
|
1400
|
+
"signup.requestFailed": "코드를 요청하지 못했어요. 연결을 확인하고 다시 시도해 주세요.",
|
|
1401
|
+
"signup.invalidCode": "인증 코드가 올바르지 않거나 만료됐어요. 다시 확인해 주세요.",
|
|
1402
|
+
"signup.recoveryUnavailable": "계정 복구 기능이 준비되지 않았어요. 잠시 후 다시 시도해 주세요.",
|
|
1403
|
+
"signup.failed": "가입을 완료하지 못했어요. 입력 내용을 확인하고 다시 시도해 주세요.",
|
|
1404
|
+
"signup.unconfirmed": "가입 결과를 확인하지 못했어요. 로그인으로 계정을 확인해 주세요.",
|
|
1405
|
+
"signup.complete": "가입이 완료됐어요. 로그인해 시작하세요.",
|
|
1406
|
+
"signup.continueLogin": "로그인해서 시작하기",
|
|
1407
|
+
"signup.terms": "계속 진행하면 서비스 이용약관에 동의하는 것으로 간주됩니다.",
|
|
1408
|
+
"signup.showPassword": "비밀번호 보기",
|
|
1409
|
+
"signup.hidePassword": "비밀번호 숨기기",
|
|
1410
|
+
"signup.enterCode": "인증 코드 입력",
|
|
1411
|
+
"signup.digit": "인증 코드 {number}번째 자리",
|
|
1412
|
+
"signup.verify": "인증하고 계속",
|
|
1413
|
+
"signup.verifying": "인증 확인 중…",
|
|
1414
|
+
"signup.previous": "이전",
|
|
1415
|
+
"signup.verifyFailed": "인증을 확인하지 못했어요. 연결을 확인하고 다시 시도해 주세요."
|
|
1338
1416
|
};
|
|
1339
1417
|
const homeTabsKo = {
|
|
1340
1418
|
"homeTabs.collaboratingTitle": "함께 작업 프로젝트",
|
|
@@ -1392,10 +1470,10 @@ const homeTabsKo = {
|
|
|
1392
1470
|
"homeTabs.startingWork": "작업 시작 중",
|
|
1393
1471
|
"homeTabs.startWorkFailed": "작업을 시작하지 못했습니다.",
|
|
1394
1472
|
"homeTabs.syncedBeforeImplementation": "구현 시작 전 워크스페이스 기획안 동기화",
|
|
1395
|
-
"homeTabs.quick": "
|
|
1396
|
-
"homeTabs.git": "저장소 가져오기",
|
|
1397
|
-
"homeTabs.zip": "
|
|
1398
|
-
"homeTabs.folder": "폴더
|
|
1473
|
+
"homeTabs.quick": "프로젝트 만들기",
|
|
1474
|
+
"homeTabs.git": "코드 저장소 가져오기",
|
|
1475
|
+
"homeTabs.zip": "Zip/파일 가져오기",
|
|
1476
|
+
"homeTabs.folder": "머신 폴더 사용하기",
|
|
1399
1477
|
"homeTabs.categories": "프로젝트 범주"
|
|
1400
1478
|
};
|
|
1401
1479
|
const navigationLoadingKo = {
|
|
@@ -1696,7 +1774,15 @@ const workAreaKo = {
|
|
|
1696
1774
|
"workArea.panelFailed": "이 화면을 표시하지 못했어요",
|
|
1697
1775
|
"workArea.panelFailedHint": "다시 시도해도 같으면 이 탭을 닫았다가 목록에서 다시 열어 주세요. 대화와 작업 내용은 그대로 남아 있어요.",
|
|
1698
1776
|
"workArea.panelRetry": "다시 시도",
|
|
1699
|
-
"workArea.panelClose": "탭 닫기"
|
|
1777
|
+
"workArea.panelClose": "탭 닫기",
|
|
1778
|
+
"workArea.nameSearch.hint": "목록 필터 · Enter로 전체 이름 검색",
|
|
1779
|
+
"workArea.nameSearch.loading": "프로젝트 파일 이름을 찾는 중…",
|
|
1780
|
+
"workArea.nameSearch.partial": "일부 파일만 확인했습니다 · Enter로 다시 검색",
|
|
1781
|
+
"workArea.nameSearch.results": "프로젝트 검색 결과 · Esc로 해제",
|
|
1782
|
+
"workArea.nameSearch.empty": "일치하는 파일 없음 · Esc로 해제",
|
|
1783
|
+
"workArea.revealFile": "파일 목록에서 위치 보기",
|
|
1784
|
+
"workArea.revealFailed": "파일 위치를 확인하지 못했습니다. 위치 보기를 다시 눌러주세요.",
|
|
1785
|
+
"workArea.fileListFailed": "목록을 불러오지 못했습니다."
|
|
1700
1786
|
};
|
|
1701
1787
|
const redesignKo = {
|
|
1702
1788
|
...sessionShareKo,
|
|
@@ -2844,7 +2930,7 @@ const personalChatsKo = {
|
|
|
2844
2930
|
"personalChats.creating": "채팅을 시작하는 중…",
|
|
2845
2931
|
"personalChats.noOnlineMachine": "온라인 작업PC가 없습니다. 채팅을 시작하려면 작업PC를 시작해주세요.",
|
|
2846
2932
|
"personalChats.localMachinePreparing": "이 컴퓨터를 Saycode에 연결하는 중이에요. 잠시 후 바로 시작할 수 있어요.",
|
|
2847
|
-
"personalChats.localMachineAsk": "
|
|
2933
|
+
"personalChats.localMachineAsk": "채팅을 시작하려면 이 컴퓨터를 작업PC로 연결해주세요.",
|
|
2848
2934
|
"personalChats.localMachineFailed": "이 컴퓨터 자동 연결에 실패했어요. 다른 방법으로 연결할 수 있어요.",
|
|
2849
2935
|
"personalChats.pickMachine": "이 채팅을 실행할 작업PC를 선택하세요",
|
|
2850
2936
|
"personalChats.createFailed": "채팅을 시작하지 못했습니다.",
|
|
@@ -4234,7 +4320,24 @@ const organizationTeamsKo = {
|
|
|
4234
4320
|
"organizationTeams.shareApply": "적용"
|
|
4235
4321
|
};
|
|
4236
4322
|
const gitAccountsKo = {
|
|
4237
|
-
"
|
|
4323
|
+
"projectStart.url": "주소로 가져오기",
|
|
4324
|
+
"projectStart.clearRepository": "저장소 선택 해제",
|
|
4325
|
+
"projectStart.providerPicker": "{provider} 저장소 선택",
|
|
4326
|
+
"projectStart.manageAccess": "저장소 접근 관리",
|
|
4327
|
+
"projectStart.repositoryPlaceholder": "저장소를 선택하세요",
|
|
4328
|
+
"projectStart.localAccount": "이 컴퓨터의 GitHub 계정 · @{account}",
|
|
4329
|
+
"projectStart.localGit": "이 컴퓨터의 Git 설정",
|
|
4330
|
+
"projectStart.machineGit": "작업PC의 Git 설정",
|
|
4331
|
+
"projectStart.githubAccount": "GitHub 연결 계정",
|
|
4332
|
+
"projectStart.gitlabAccount": "GitLab 연결 계정",
|
|
4333
|
+
"projectStart.organizationPat": "조직 PAT · {name}",
|
|
4334
|
+
"projectStart.personalPat": "개인 PAT",
|
|
4335
|
+
"projectStart.selectedConnectionHint": "선택한 저장소의 연결 계정을 사용합니다.",
|
|
4336
|
+
"projectStart.localAccountHint": "이 컴퓨터에 로그인한 GitHub 계정으로 가져옵니다.",
|
|
4337
|
+
"projectStart.machineGitHint": "작업PC에 설정된 Git 인증을 사용합니다.",
|
|
4338
|
+
"projectStart.accountHint": "선택한 연결 계정으로 저장소에 접근합니다.",
|
|
4339
|
+
"projectStart.connectionUnavailable": "선택한 저장소 연결을 사용할 수 없습니다. 연결 상태를 다시 확인해 주세요.",
|
|
4340
|
+
"settingsTab.gitAccounts": "저장소",
|
|
4238
4341
|
"gitAccounts.title": "GitHub · GitLab 계정",
|
|
4239
4342
|
"gitAccounts.description": "저장소 가져오기·트리거·PR 작업에 쓸 계정을 연결합니다. 토큰은 서버에만 보관되고 이 기기에는 저장되지 않습니다.",
|
|
4240
4343
|
"gitAccounts.refresh": "상태 새로고침",
|
|
@@ -4250,7 +4353,27 @@ const gitAccountsKo = {
|
|
|
4250
4353
|
"gitAccounts.connectFailed": "연결을 시작하지 못했습니다. 잠시 후 다시 시도하세요.",
|
|
4251
4354
|
"gitAccounts.disconnectFailed": "연결을 해제하지 못했습니다. 잠시 후 다시 시도하세요.",
|
|
4252
4355
|
"gitAccounts.warnings": "해제는 완료됐지만 확인이 필요한 항목이 있습니다:",
|
|
4253
|
-
"
|
|
4356
|
+
"projectStart.repositorySettings": "저장소 설정",
|
|
4357
|
+
"projectStart.allRepositories": "모든 저장소",
|
|
4358
|
+
"projectStart.selectedRepositories": "선택한 저장소",
|
|
4359
|
+
"projectStart.lastConnected": "마지막 연결",
|
|
4360
|
+
"projectStart.patTitle": "개인 코드 저장소 Personal Access Token",
|
|
4361
|
+
"projectStart.patIssue": "PAT 발급 페이지 열기",
|
|
4362
|
+
"projectStart.patRegistered": "등록됨 · 개인",
|
|
4363
|
+
"projectStart.patUnregistered": "등록 안 됨",
|
|
4364
|
+
"projectStart.patUpdatedAt": "마지막 갱신",
|
|
4365
|
+
"projectStart.patConfirmDelete": "삭제 확인",
|
|
4366
|
+
"projectStart.patDelete": "삭제",
|
|
4367
|
+
"projectStart.patUpdate": "새 PAT (갱신)",
|
|
4368
|
+
"projectStart.patNew": "새 PAT",
|
|
4369
|
+
"projectStart.patPlaceholder": "토큰 값을 입력하세요",
|
|
4370
|
+
"projectStart.patHide": "토큰 숨기기",
|
|
4371
|
+
"projectStart.patShow": "토큰 표시",
|
|
4372
|
+
"projectStart.patSaveUpdate": "갱신",
|
|
4373
|
+
"projectStart.patSaveNew": "등록",
|
|
4374
|
+
"projectStart.patSaved": "토큰을 저장했습니다.",
|
|
4375
|
+
"projectStart.patDeleted": "토큰을 삭제했습니다.",
|
|
4376
|
+
"projectStart.loadingConnections": "저장소 연결 정보 불러오는 중…"
|
|
4254
4377
|
};
|
|
4255
4378
|
const agentBrowserConsoleKo = {
|
|
4256
4379
|
"browserPanel.agentBrowserConsole": "AI 브라우저 작업 콘솔",
|
|
@@ -4821,6 +4944,51 @@ const designGuideKo = {
|
|
|
4821
4944
|
"designGuide.entry.body": "앱 화면을 만들 때 쓰는 색, 글자, 부품, 대화 화면 기준을 확인합니다.",
|
|
4822
4945
|
"designGuide.entry.open": "디자인 가이드 열기"
|
|
4823
4946
|
};
|
|
4947
|
+
const prFollowThroughKo = {
|
|
4948
|
+
"prFollow.readyTitle": "PR #{number} 머지 가능",
|
|
4949
|
+
"prFollow.readyBody": "CI·리뷰 봇·충돌 확인을 모두 통과했습니다. 머지는 직접 해 주세요.",
|
|
4950
|
+
"prFollow.mergeGuard.stacked": "이 PR 브랜치에 아직 반영되지 않은 스택 PR이 있습니다: {list}. 지금 머지하면 그 PR은 고아가 되어 따로 옮겨야 합니다.",
|
|
4951
|
+
"prFollow.mergeGuard.botReviewing": "리뷰 봇이 아직 이 PR을 검토하거나 수정 PR을 만드는 중입니다.",
|
|
4952
|
+
"prFollow.mergeGuard.unknown": "남은 스택 PR이 있는지 확인하지 못했습니다.",
|
|
4953
|
+
"prFollow.mergeGuard.confirm": "그래도 머지할까요?",
|
|
4954
|
+
"prFollow.mergeGuard.title": "머지 전에 확인해 주세요",
|
|
4955
|
+
"prFollow.mergeGuard.mergeAnyway": "그래도 머지",
|
|
4956
|
+
"prFollow.enable": "PR 지킴이",
|
|
4957
|
+
"prFollow.enableTitle": "이 PR의 CI·리뷰 봇·충돌을 지켜보다가 대화가 쉬고 있을 때 고칠 일을 대신 지시합니다. 머지 가능해지면 알려 줍니다.",
|
|
4958
|
+
"prFollow.disable": "PR 지킴이 끄기",
|
|
4959
|
+
"prFollow.dismiss": "PR 지킴이 기록 지우기",
|
|
4960
|
+
"prFollow.badge.prefix": "지킴이 · {status}",
|
|
4961
|
+
"prFollow.badge.awaitingPr": "PR 기다리는 중",
|
|
4962
|
+
"prFollow.badge.watching": "지켜보는 중",
|
|
4963
|
+
"prFollow.badge.checksPending": "체크 대기",
|
|
4964
|
+
"prFollow.badge.botReviewing": "리뷰 봇 대기",
|
|
4965
|
+
"prFollow.badge.conflict": "충돌",
|
|
4966
|
+
"prFollow.badge.checksFailed": "CI 실패",
|
|
4967
|
+
"prFollow.badge.stackedPr": "스택 PR 있음",
|
|
4968
|
+
"prFollow.badge.botFindings": "봇 지적 반영 필요",
|
|
4969
|
+
"prFollow.badge.ready": "머지 가능",
|
|
4970
|
+
"prFollow.badge.merged": "머지됨",
|
|
4971
|
+
"prFollow.badge.lookupFailed": "PR 상태 확인 실패",
|
|
4972
|
+
"prFollow.badge.stopped.user": "꺼짐",
|
|
4973
|
+
"prFollow.badge.stopped.noPr": "PR이 만들어지지 않음",
|
|
4974
|
+
"prFollow.badge.stopped.closed": "PR 닫힘",
|
|
4975
|
+
"prFollow.badge.stopped.budget": "자동 지시 한도 도달",
|
|
4976
|
+
"prFollow.badge.stopped.repeatedFailure": "같은 실패 반복 — 확인 필요",
|
|
4977
|
+
"prFollow.badge.stopped.sessionRemoved": "대화 없음",
|
|
4978
|
+
"prFollow.settings.saveFailed": "설정을 저장하지 못했습니다.",
|
|
4979
|
+
"prFollow.settings.followThrough": "오토파일럿 Commit & PR 뒤 PR 지킴이 켜기",
|
|
4980
|
+
"prFollow.settings.followThroughHelp": "PR의 CI 실패·리뷰 봇 수정 PR·충돌을 1분마다 확인하고, 대화가 쉬고 있을 때 고칠 일을 대신 지시합니다. 머지 가능해지면 알림을 보냅니다. base 브랜치로의 머지는 하지 않습니다.",
|
|
4981
|
+
"prFollow.settings.continueAfterMerge": "머지되면 남은 작업 이어가기",
|
|
4982
|
+
"prFollow.settings.continueAfterMergeHelp": "PR이 머지되면 같은 대화에 spec의 남은 작업을 최신 base 기준 새 브랜치에서 이어가라고 지시합니다. 남은 스택 PR이 있으면 그 처리도 함께 지시합니다.",
|
|
4983
|
+
"prFollow.settings.autoAnswer": "오토파일럿이 추천안 질문에 대신 답하기",
|
|
4984
|
+
"prFollow.settings.autoAnswerHelp": '오토파일럿 중에 AI가 추천안을 제시하며 "진행할까요?"로 멈추면 "추천안대로 진행"으로 답합니다(한 번 켤 때 최대 2회). 권한·비용·외부 전송·되돌리기 어려운 변경은 진행하지 말라고 함께 지시합니다.',
|
|
4985
|
+
"prFollow.settings.reviewer": "오토파일럿 독립 리뷰 AI",
|
|
4986
|
+
"prFollow.settings.reviewerDefault": "기본값",
|
|
4987
|
+
"prFollow.settings.reviewerModel": "리뷰 모델",
|
|
4988
|
+
"prFollow.settings.reviewerEffort": "리뷰 Effort",
|
|
4989
|
+
"prFollow.settings.reviewerHelp": "오토파일럿을 독립 리뷰로 켤 때 쓸 AI입니다. 켤 때의 값이 그 실행에 고정됩니다.",
|
|
4990
|
+
"prFollow.badge.title": "PR 지킴이 · {status}"
|
|
4991
|
+
};
|
|
4824
4992
|
const catalogExtensionsKo = {
|
|
4825
4993
|
...chatSchedulesKo,
|
|
4826
4994
|
...mySettingsKo,
|
|
@@ -4901,9 +5069,20 @@ const catalogExtensionsKo = {
|
|
|
4901
5069
|
...lessonCandidateKo,
|
|
4902
5070
|
...gitAccountsKo,
|
|
4903
5071
|
...agentBrowserConsoleKo,
|
|
4904
|
-
...designGuideKo
|
|
5072
|
+
...designGuideKo,
|
|
5073
|
+
...prFollowThroughKo
|
|
4905
5074
|
};
|
|
4906
5075
|
const onboardingKo = {
|
|
5076
|
+
"folderBrowser.title": "폴더 선택",
|
|
5077
|
+
"folderBrowser.parent": "상위 폴더",
|
|
5078
|
+
"folderBrowser.folders": "폴더 목록",
|
|
5079
|
+
"folderBrowser.loading": "폴더 확인 중…",
|
|
5080
|
+
"folderBrowser.empty": "하위 폴더가 없습니다.",
|
|
5081
|
+
"folderBrowser.newPath": "새로운 경로에서 시작",
|
|
5082
|
+
"folderBrowser.choose": "이 폴더 선택",
|
|
5083
|
+
"projectImport.drop": "zip · 폴더 · 파일을 끌어다 놓거나 클릭해서 선택",
|
|
5084
|
+
"projectImport.excludeHint": "node_modules / .git / dist 등 무거운 폴더는 자동으로 제외돼요",
|
|
5085
|
+
"projectImport.folder": "폴더 선택",
|
|
4907
5086
|
"onboarding.page.instructions": "Saycode 지시 사용",
|
|
4908
5087
|
"onboarding.instructions.body": "AI가 작업을 정리하고 결과를 안내할 때 Saycode의 기본 지시를 사용할까요?",
|
|
4909
5088
|
"onboarding.instructions.enable": "지시 사용하기",
|
|
@@ -5103,7 +5282,8 @@ const onboardingKo = {
|
|
|
5103
5282
|
"onboarding.groups.error.capabilityRequired": "이 머신의 Saycode CLI가 조직 인증 세트를 지원하지 않습니다. CLI를 업데이트한 뒤 다시 시도해 주세요.",
|
|
5104
5283
|
"onboarding.groups.error.setupTokenRuntimeRequired": "이 컴퓨터에 조직용 Claude setup-token runtime이 없습니다. Saycode가 제공한 cswap runtime을 설치한 뒤 다시 시도해 주세요.",
|
|
5105
5284
|
"onboarding.groups.error.happyDaemonUpdateRequired": "이 컴퓨터의 Saycode 데몬이 조직 인증 세트를 지원하지 않습니다. Desktop을 업데이트한 뒤 다시 시도해 주세요.",
|
|
5106
|
-
"onboarding.groups.error.unknown": "조직 인증 세트를 연결하지 못했습니다. 잠시 후 다시 시도해 주세요."
|
|
5285
|
+
"onboarding.groups.error.unknown": "조직 인증 세트를 연결하지 못했습니다. 잠시 후 다시 시도해 주세요.",
|
|
5286
|
+
"folderBrowser.path": "경로"
|
|
5107
5287
|
};
|
|
5108
5288
|
const standaloneRemoteOnboardingKo = {
|
|
5109
5289
|
"onboarding.optional": " (선택)",
|
|
@@ -5188,6 +5368,7 @@ const ko = {
|
|
|
5188
5368
|
"assetWorkflow.filesSelect": "직접 선택한 파일 읽기",
|
|
5189
5369
|
"assetWorkflow.draftsPrepare": "선택한 대화의 초안과 첨부 준비",
|
|
5190
5370
|
...workKo,
|
|
5371
|
+
...workTemplateKo,
|
|
5191
5372
|
...FILE_ACCESS_TRANSLATIONS_KO,
|
|
5192
5373
|
...chatWorkKo,
|
|
5193
5374
|
...usageEntryKo,
|
|
@@ -7061,7 +7242,7 @@ const ko = {
|
|
|
7061
7242
|
"saycodePromptPreference.eyebrow": "AI도구 지시 선택",
|
|
7062
7243
|
"saycodePromptPreference.title": "Saycode 기본 지시를 사용할까요?",
|
|
7063
7244
|
"saycodePromptPreference.body": "내가 만든 Claude/Codex 스킬과 하네스를 그대로 쓰려면 꺼진 상태를 유지하세요. 나중에 환경설정에서 언제든 바꿀 수 있습니다.",
|
|
7064
|
-
"saycodePromptPreference.defaultOnNotice": "자식 AI도구 호출, 내부 작업 위임, 결과물 인라인
|
|
7245
|
+
"saycodePromptPreference.defaultOnNotice": "자식 AI도구 호출, 내부 작업 위임, 결과물 인라인 미리보기, Saycode 사용 안내는 기본으로 계속 켜집니다. 이 기능도 끄려면 환경설정 → 시스템 프롬프트에서 각각 해제하세요.",
|
|
7065
7246
|
"saycodePromptPreference.keepOff": "꺼진 상태 유지",
|
|
7066
7247
|
"saycodePromptPreference.enable": "Saycode 지시 켜기",
|
|
7067
7248
|
"saycodePromptPreference.saving": "저장 중…",
|
|
@@ -7070,7 +7251,7 @@ const ko = {
|
|
|
7070
7251
|
"saycodePromptPreference.settingsEnable": "Saycode가 제공하는 기본 행동 지시 사용",
|
|
7071
7252
|
"saycodePromptPreference.settingsHelp": "사용자 시스템 프롬프트, 프로젝트 지침, 설치한 스킬과 하네스는 이 설정과 관계없이 유지됩니다.",
|
|
7072
7253
|
"saycodePromptPreference.blocksTitle": "개별 지시 선택",
|
|
7073
|
-
"saycodePromptPreference.blocksHelp": "자식 AI도구 호출, 내부 작업 위임, 결과물 인라인
|
|
7254
|
+
"saycodePromptPreference.blocksHelp": "자식 AI도구 호출, 내부 작업 위임, 결과물 인라인 미리보기, Saycode 사용 안내는 기본으로 켜지며, 나머지 항목은 위의 전체 설정을 따릅니다.",
|
|
7074
7255
|
"saycodePromptPreference.alwaysOnNotice": "채팅 제목 생성은 항상 켜져 있으며 끌 수 없습니다. 채팅 목록에서 대화를 다시 찾으려면 필요합니다.",
|
|
7075
7256
|
"saycodePromptPreference.block.agentOrchestration.label": "자식 AI도구 호출",
|
|
7076
7257
|
"saycodePromptPreference.block.agentOrchestration.description": "자연어로 요청한 작업을 독립된 자식 AI도구 세션에 맡기고 결과를 회수하도록 지시합니다.",
|
|
@@ -7082,6 +7263,8 @@ const ko = {
|
|
|
7082
7263
|
"saycodePromptPreference.block.coAuthoredCredit.description": "커밋 메시지에 Claude와 Happy 공동 작성자 표시를 추가합니다.",
|
|
7083
7264
|
"saycodePromptPreference.block.inlinePreview.label": "결과물 인라인 미리보기",
|
|
7084
7265
|
"saycodePromptPreference.block.inlinePreview.description": "HTML 결과물을 만들면 답변 안에 미리보기를 띄우도록 지시합니다.",
|
|
7266
|
+
"saycodePromptPreference.block.saycodeGuide.label": "Saycode 사용 안내",
|
|
7267
|
+
"saycodePromptPreference.block.saycodeGuide.description": "Saycode 사용법을 물으면 관련 안내와 앱 상황 일부(버전·사용 형태·화면 표시 설정)를 이번 대화의 AI에게 함께 보냅니다. 전체 안내서는 대화 작업 폴더의 .aplus/saycode-guide에 두며 저장소에는 올라가지 않습니다. 질문인지 판단하는 일은 이 기기에서만 합니다.",
|
|
7085
7268
|
"saycodePromptPreference.loading": "계정 설정을 불러오는 중…",
|
|
7086
7269
|
"standalone.zrokStatus": "zrok 상태",
|
|
7087
7270
|
"standalone.zrokNeeded": "zrok2 설정이 필요합니다",
|
|
@@ -8476,10 +8659,10 @@ In a chat
|
|
|
8476
8659
|
/model · /effort · /voice [path] · /detach · /exit live chat commands
|
|
8477
8660
|
|
|
8478
8661
|
Leaving never stops a remote session — it keeps running on the machine.`;
|
|
8479
|
-
const record$
|
|
8662
|
+
const record$l = (value2) => !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
8480
8663
|
const ids = (value2) => Array.isArray(value2) && value2.length <= 1e3 && value2.every((id2) => typeof id2 === "string" && id2.trim().length > 0);
|
|
8481
8664
|
function parseMachineSharing(value2) {
|
|
8482
|
-
if (!record$
|
|
8665
|
+
if (!record$l(value2) || !record$l(value2.machine)) throw new Error("Invalid machine sharing response");
|
|
8483
8666
|
const m = value2.machine;
|
|
8484
8667
|
if (typeof value2.revision !== "string" || !value2.revision || !["canShare", "canMakeDedicated", "canManageAccess"].every((key) => typeof value2[key] === "boolean") || !["machineId", "ownerId", "companyId", "infraMode"].every((key) => typeof m[key] === "string" && !!m[key]) || !["dedicated", "shared"].includes(String(m.type)) || !ids(m.accessList) || m.accessList.length === 0 || m.accessList.includes("*") && m.accessList.length !== 1 || m.assignedUserId != null && typeof m.assignedUserId !== "string" || value2.missingUserIds !== void 0 && !ids(value2.missingUserIds)) throw new Error("Invalid machine sharing response");
|
|
8485
8668
|
return {
|
|
@@ -8744,7 +8927,7 @@ function parseCompanyMemberDirectoryPage(value2) {
|
|
|
8744
8927
|
nextCursor: rawCursor
|
|
8745
8928
|
};
|
|
8746
8929
|
}
|
|
8747
|
-
function record$
|
|
8930
|
+
function record$k(value2) {
|
|
8748
8931
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("Invalid team collaboration response");
|
|
8749
8932
|
return value2;
|
|
8750
8933
|
}
|
|
@@ -8757,12 +8940,12 @@ function choice(value2, values) {
|
|
|
8757
8940
|
return value2;
|
|
8758
8941
|
}
|
|
8759
8942
|
function parseCollaboration(value2) {
|
|
8760
|
-
const row2 = record$
|
|
8943
|
+
const row2 = record$k(value2);
|
|
8761
8944
|
if (!Array.isArray(row2.teamIds) || row2.teamIds.length > 1e3 || !Array.isArray(row2.bindings) || row2.bindings.length > 1e3) throw new Error("Invalid team collaboration list");
|
|
8762
8945
|
return {
|
|
8763
8946
|
teamIds: row2.teamIds.map(identifier$4),
|
|
8764
8947
|
bindings: row2.bindings.map((value22) => {
|
|
8765
|
-
const binding = record$
|
|
8948
|
+
const binding = record$k(value22);
|
|
8766
8949
|
return {
|
|
8767
8950
|
accountId: identifier$4(binding.accountId),
|
|
8768
8951
|
membershipOrigin: choice(binding.membershipOrigin, ["unresolved", "team", "preexisting"]),
|
|
@@ -8774,8 +8957,8 @@ function parseCollaboration(value2) {
|
|
|
8774
8957
|
};
|
|
8775
8958
|
}
|
|
8776
8959
|
function parseMutation(value2) {
|
|
8777
|
-
const row2 = record$
|
|
8778
|
-
const sync = row2.sync === null ? null : record$
|
|
8960
|
+
const row2 = record$k(value2);
|
|
8961
|
+
const sync = row2.sync === null ? null : record$k(row2.sync);
|
|
8779
8962
|
if (sync && [sync.synced, sync.failed, sync.rejected].some((value22) => !Number.isSafeInteger(value22) || Number(value22) < 0)) throw new Error("Invalid team synchronization counts");
|
|
8780
8963
|
return { collaboration: parseCollaboration(row2.collaboration), sync, ...parseProjectMemberRemovalResult(row2) };
|
|
8781
8964
|
}
|
|
@@ -8783,7 +8966,7 @@ function createProjectTeamCollaborationApi(request2) {
|
|
|
8783
8966
|
const path2 = (id2) => `/api/projects/${encodeURIComponent(id2)}/collaboration/teams`;
|
|
8784
8967
|
return {
|
|
8785
8968
|
async getProjectTeamCollaboration(id2) {
|
|
8786
|
-
return parseCollaboration(record$
|
|
8969
|
+
return parseCollaboration(record$k(await request2(path2(id2))).collaboration);
|
|
8787
8970
|
},
|
|
8788
8971
|
async updateProjectTeamCollaboration(id2, teamIds) {
|
|
8789
8972
|
if (teamIds.length > 1e3) throw new Error("Too many collaboration teams");
|
|
@@ -8795,6 +8978,27 @@ function createProjectTeamCollaborationApi(request2) {
|
|
|
8795
8978
|
}
|
|
8796
8979
|
};
|
|
8797
8980
|
}
|
|
8981
|
+
function record$j(value2) {
|
|
8982
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("Invalid organization enrollment response");
|
|
8983
|
+
return value2;
|
|
8984
|
+
}
|
|
8985
|
+
function parseResult(value2, kind) {
|
|
8986
|
+
const row2 = record$j(value2);
|
|
8987
|
+
if (row2.reauthenticationRequired === true) return { companyId: null, reauthenticationRequired: true };
|
|
8988
|
+
const id2 = kind === "join" ? record$j(row2.member).companyId : row2.id;
|
|
8989
|
+
if (typeof id2 !== "string" || !id2.trim()) throw new Error("Invalid organization enrollment response");
|
|
8990
|
+
return { companyId: id2, reauthenticationRequired: false };
|
|
8991
|
+
}
|
|
8992
|
+
function createCompanyEnrollmentApi(request2) {
|
|
8993
|
+
return {
|
|
8994
|
+
async joinCompanyByCode(joinCode, mfaSessionToken) {
|
|
8995
|
+
return parseResult(await request2("/api/companies/join", { method: "POST", body: JSON.stringify({ joinCode: joinCode.trim() }), headers: mfaSessionToken ? { "X-Saycode-Mfa-Session": mfaSessionToken } : {} }), "join");
|
|
8996
|
+
},
|
|
8997
|
+
async createCompany(input) {
|
|
8998
|
+
return parseResult(await request2("/api/companies", { method: "POST", body: JSON.stringify({ displayName: input.displayName.trim(), slug: input.slug.trim() }) }), "create");
|
|
8999
|
+
}
|
|
9000
|
+
};
|
|
9001
|
+
}
|
|
8798
9002
|
const AI_GROUP_ERROR_KINDS = [
|
|
8799
9003
|
"AI_GROUP_ACCOUNT_IDENTITY_UNAVAILABLE",
|
|
8800
9004
|
"AI_GROUP_ACCOUNT_KEY_LOCKED",
|
|
@@ -8856,7 +9060,7 @@ function parseAiGroupErrorKind(value2) {
|
|
|
8856
9060
|
return typeof value2 === "string" && AI_GROUP_ERROR_KINDS.includes(value2) ? value2 : "UNKNOWN_ERROR";
|
|
8857
9061
|
}
|
|
8858
9062
|
const MANAGED_ACCOUNT_ID = /^[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/i;
|
|
8859
|
-
function record$
|
|
9063
|
+
function record$i(value2) {
|
|
8860
9064
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("Invalid AI group metadata");
|
|
8861
9065
|
return value2;
|
|
8862
9066
|
}
|
|
@@ -8885,23 +9089,23 @@ function receiptMetadata(result2) {
|
|
|
8885
9089
|
...result2.assignmentGeneration == null ? {} : { assignmentGeneration: positiveInteger(result2.assignmentGeneration) },
|
|
8886
9090
|
...result2.fingerprint == null ? {} : { fingerprint: digest$2(result2.fingerprint) },
|
|
8887
9091
|
...result2.appliedCredentials == null ? {} : { appliedCredentials: list$1(result2.appliedCredentials, 500).map((entry) => {
|
|
8888
|
-
const credential = record$
|
|
9092
|
+
const credential = record$i(entry), managedAccountId = text$9(credential.managedAccountId, 80);
|
|
8889
9093
|
if (!MANAGED_ACCOUNT_ID.test(managedAccountId)) throw new Error("Invalid AI group metadata");
|
|
8890
9094
|
return { managedAccountId, credentialGeneration: positiveInteger(credential.credentialGeneration) };
|
|
8891
9095
|
}) }
|
|
8892
9096
|
};
|
|
8893
9097
|
}
|
|
8894
9098
|
function parseAiGroupAssignments(value2, companyId) {
|
|
8895
|
-
const body = record$
|
|
9099
|
+
const body = record$i(value2);
|
|
8896
9100
|
if (body.version !== 1 || body.companyId !== companyId) throw new Error("Invalid AI group scope");
|
|
8897
9101
|
return { version: 1, companyId, groups: list$1(body.groups, 1e3).map((value22) => {
|
|
8898
|
-
const group = record$
|
|
9102
|
+
const group = record$i(value22);
|
|
8899
9103
|
return {
|
|
8900
9104
|
credentialGroupId: text$9(group.credentialGroupId, 128),
|
|
8901
9105
|
credentialGroupName: text$9(group.credentialGroupName, 80),
|
|
8902
9106
|
userGroupName: text$9(group.userGroupName, 80),
|
|
8903
9107
|
accounts: list$1(group.accounts, 500).map((value3) => {
|
|
8904
|
-
const account = record$
|
|
9108
|
+
const account = record$i(value3), identity = text$9(account.identity);
|
|
8905
9109
|
if (!/^[a-f0-9]{64}$/.test(identity) || !Number.isSafeInteger(account.bundleVersion) || Number(account.bundleVersion) < 1 || account.kind !== "oauth" && account.kind !== "api_key") throw new Error("Invalid AI group account");
|
|
8906
9110
|
const managed = account.credentialType === "setup_token";
|
|
8907
9111
|
if (managed && (account.provider !== "claude" || account.kind !== "oauth" || typeof account.managedAccountId !== "string" || !MANAGED_ACCOUNT_ID.test(account.managedAccountId) || !Number.isSafeInteger(account.credentialGeneration) || Number(account.credentialGeneration) < 1)) throw new Error("Invalid AI group managed account");
|
|
@@ -8917,7 +9121,7 @@ function parseAiGroupAssignments(value2, companyId) {
|
|
|
8917
9121
|
})
|
|
8918
9122
|
};
|
|
8919
9123
|
}), results: list$1(body.results, 5e3).map((value22) => {
|
|
8920
|
-
const result2 = record$
|
|
9124
|
+
const result2 = record$i(value22), status = result2.status;
|
|
8921
9125
|
if (status !== "pending" && status !== "applied" && status !== "revoked" && status !== "failed" && status !== "action_required") throw new Error("Invalid AI group result");
|
|
8922
9126
|
const errorKind = parseAiGroupErrorKind(result2.errorKind);
|
|
8923
9127
|
return {
|
|
@@ -9047,15 +9251,15 @@ function createClaudeCollectorApi(transport) {
|
|
|
9047
9251
|
}
|
|
9048
9252
|
};
|
|
9049
9253
|
}
|
|
9050
|
-
const record$
|
|
9254
|
+
const record$h = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
9051
9255
|
const timestamp$1 = (value2) => Number.isSafeInteger(value2) && value2 >= 0;
|
|
9052
9256
|
function parseWindow(value2) {
|
|
9053
|
-
const row2 = record$
|
|
9257
|
+
const row2 = record$h(value2);
|
|
9054
9258
|
if (!row2 || typeof row2.id !== "string" || !row2.id || !timestamp$1(row2.openedAt) || !timestamp$1(row2.endsAt) || !timestamp$1(row2.waitMinutes)) return null;
|
|
9055
9259
|
return { id: row2.id, openedAt: row2.openedAt, endsAt: row2.endsAt, waitMinutes: row2.waitMinutes };
|
|
9056
9260
|
}
|
|
9057
9261
|
function parseMe(value2) {
|
|
9058
|
-
const row2 = record$
|
|
9262
|
+
const row2 = record$h(value2);
|
|
9059
9263
|
if (!row2) return null;
|
|
9060
9264
|
const { hasLatestGrant, pending: pending2, autoEligible, autoAt, held } = row2;
|
|
9061
9265
|
if (typeof hasLatestGrant !== "boolean" || typeof pending2 !== "boolean" || typeof autoEligible !== "boolean" || typeof held !== "boolean") return null;
|
|
@@ -9066,7 +9270,7 @@ function companyKeyAutoWindowHeaders(options) {
|
|
|
9066
9270
|
return options?.distributor ? { "X-Saycode-Auto-Grant-Client": "desktop" } : {};
|
|
9067
9271
|
}
|
|
9068
9272
|
function parseCompanyKeyAutoWindow(raw) {
|
|
9069
|
-
const body = record$
|
|
9273
|
+
const body = record$h(raw);
|
|
9070
9274
|
return { window: parseWindow(body?.window), me: parseMe(body?.me) };
|
|
9071
9275
|
}
|
|
9072
9276
|
function companyKeyGrantFailure(error) {
|
|
@@ -9076,7 +9280,7 @@ function companyKeyGrantFailure(error) {
|
|
|
9076
9280
|
const body = "body" in error ? error.body : void 0;
|
|
9077
9281
|
if (typeof body === "string") {
|
|
9078
9282
|
try {
|
|
9079
|
-
const parsed2 = record$
|
|
9283
|
+
const parsed2 = record$h(JSON.parse(body));
|
|
9080
9284
|
if (typeof parsed2?.error === "string") reason = parsed2.error;
|
|
9081
9285
|
} catch {
|
|
9082
9286
|
}
|
|
@@ -11747,32 +11951,32 @@ function invalidateRendererQueries(kind, scope2) {
|
|
|
11747
11951
|
}
|
|
11748
11952
|
const ROUTING_POLICY = "org-shared-difficulty-routing.v1";
|
|
11749
11953
|
const ROUTING_AGENTS = ["claude", "codex"];
|
|
11750
|
-
function record$
|
|
11954
|
+
function record$g(value2) {
|
|
11751
11955
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
11752
11956
|
}
|
|
11753
11957
|
function id$2(value2) {
|
|
11754
11958
|
return typeof value2 === "string" && value2.length > 0 && value2.length <= 256;
|
|
11755
11959
|
}
|
|
11756
11960
|
function parseRoutingResult(value2) {
|
|
11757
|
-
const r = record$
|
|
11758
|
-
const baseRoute = record$
|
|
11759
|
-
const previousApplied = record$
|
|
11961
|
+
const r = record$g(value2);
|
|
11962
|
+
const baseRoute = record$g(r?.baseRoute);
|
|
11963
|
+
const previousApplied = record$g(r?.previousApplied);
|
|
11760
11964
|
if (!r || r.version !== 1 || r.mode !== "auto" || r.policyVersion !== ROUTING_POLICY || !id$2(r.clientRequestId) || !id$2(r.model) || !(r.effort === null || id$2(r.effort)) || !(r.policyRevision === null || Number.isSafeInteger(r.policyRevision) && r.policyRevision >= 0) || !["trivial", "routine", "hard", "escalated"].includes(String(r.difficulty)) || !["p1-local", "p2-org-shared", "p2-local", "fallback-p1", "manual-legacy"].includes(String(r.classifierSource)) || r.stage !== void 0 && !["selected", "queued", "applied", "failed", "cancelled", "unknown"].includes(String(r.stage)) || r.revision !== void 0 && (!Number.isSafeInteger(r.revision) || r.revision < 0) || r.evidence !== void 0 && !["engine-applied", "legacy-selection", "unknown"].includes(String(r.evidence)) || r.executionId !== void 0 && !id$2(r.executionId) || r.clientRequestIds !== void 0 && (!Array.isArray(r.clientRequestIds) || r.clientRequestIds.length > 256 || !r.clientRequestIds.every(id$2)) || r.candidateDifficulty !== void 0 && !["trivial", "routine", "hard", "escalated"].includes(String(r.candidateDifficulty)) || r.baseRoute !== void 0 && (!baseRoute || !["trivial", "routine", "hard", "escalated"].includes(String(baseRoute.difficulty)) || !id$2(baseRoute.model) || !(baseRoute.effort === null || id$2(baseRoute.effort))) || r.temporaryEscalation !== void 0 && typeof r.temporaryEscalation !== "boolean" || r.decisionReasons !== void 0 && (!Array.isArray(r.decisionReasons) || r.decisionReasons.length > 16 || !r.decisionReasons.every((reason) => typeof reason === "string" && reason.length > 0 && reason.length <= 64)) || r.providerConfirmedModel !== void 0 && !id$2(r.providerConfirmedModel) || r.previousApplied !== void 0 && (!previousApplied || !id$2(previousApplied.model) || !(previousApplied.effort === null || id$2(previousApplied.effort)) || !["trivial", "routine", "hard", "escalated"].includes(String(previousApplied.difficulty)) || !["auto", "local-auto-bootstrap", "manual"].includes(String(previousApplied.kind)))) return null;
|
|
11761
11965
|
return r;
|
|
11762
11966
|
}
|
|
11763
11967
|
function parseRoutingPolicy(value2) {
|
|
11764
|
-
const p = record$
|
|
11968
|
+
const p = record$g(value2);
|
|
11765
11969
|
if (!p || p.schemaVersion !== 1 || typeof p.enabled !== "boolean" || p.policyVersion !== ROUTING_POLICY || !Number.isSafeInteger(p.revision) || p.revision < 0 || typeof p.appliesToSharedMachines !== "boolean" || !(p.hostMachineId === null || id$2(p.hostMachineId)) || !["off", "ready", "host-offline", "host-unsupported", "not-authorized"].includes(String(p.status))) return null;
|
|
11766
11970
|
if (p.enabled && !id$2(p.hostMachineId)) return null;
|
|
11767
11971
|
return p;
|
|
11768
11972
|
}
|
|
11769
11973
|
function parseRoutingSettings(value2) {
|
|
11770
|
-
const v = record$
|
|
11974
|
+
const v = record$g(value2), s = record$g(v?.settings), r = record$g(v?.rollout), h = v?.host === null ? null : record$g(v?.host);
|
|
11771
11975
|
if (!s || s.schemaVersion !== 1 || s.policyVersion !== ROUTING_POLICY || typeof s.enabled !== "boolean" || !(s.hostMachineId === null || id$2(s.hostMachineId)) || s.enabled && !id$2(s.hostMachineId) || !Number.isSafeInteger(s.revision) || s.revision < 0 || !(s.updatedById === null || id$2(s.updatedById)) || !(s.updatedAt === null || typeof s.updatedAt === "number" && Number.isFinite(s.updatedAt)) || !r || !["sharedMachineCount", "capableSharedMachineCount", "unsupportedSharedMachineCount"].every((k) => Number.isSafeInteger(r[k]) && r[k] >= 0) || v?.host !== null && (!h || !id$2(h.machineId) || !(h.displayName === null || typeof h.displayName === "string") || typeof h.online !== "boolean" || typeof h.shared !== "boolean" || !["ready", "missing", "unsupported", "not-ready"].includes(String(h.capability)) || !(h.hostProcessKeyId === null || id$2(h.hostProcessKeyId)))) throw new Error("Invalid organization routing settings");
|
|
11772
11976
|
return v;
|
|
11773
11977
|
}
|
|
11774
11978
|
function canNegotiateOrgRouting(input) {
|
|
11775
|
-
const capability = record$
|
|
11979
|
+
const capability = record$g(input.machine?.difficultyRouting);
|
|
11776
11980
|
return !input.manual && id$2(input.requestId) && !!input.companyId && ROUTING_AGENTS.includes(input.agent ?? "") && input.machine?.registry?.type === "shared" && input.machine.registry.companyId === input.companyId && capability?.version === 1 && capability.protocol === ROUTING_POLICY;
|
|
11777
11981
|
}
|
|
11778
11982
|
function prepareRoutingIntent(input) {
|
|
@@ -11970,9 +12174,9 @@ const globalConfig = {};
|
|
|
11970
12174
|
function config(newConfig) {
|
|
11971
12175
|
return globalConfig;
|
|
11972
12176
|
}
|
|
11973
|
-
function getEnumValues(
|
|
11974
|
-
const numericValues = Object.values(
|
|
11975
|
-
const values = Object.entries(
|
|
12177
|
+
function getEnumValues(entries2) {
|
|
12178
|
+
const numericValues = Object.values(entries2).filter((v) => typeof v === "number");
|
|
12179
|
+
const values = Object.entries(entries2).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);
|
|
11976
12180
|
return values;
|
|
11977
12181
|
}
|
|
11978
12182
|
function jsonStringifyReplacer(_, value2) {
|
|
@@ -15235,7 +15439,7 @@ const ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
|
|
|
15235
15439
|
inst.keyType = def.keyType;
|
|
15236
15440
|
inst.valueType = def.valueType;
|
|
15237
15441
|
});
|
|
15238
|
-
function record$
|
|
15442
|
+
function record$f(keyType, valueType, params) {
|
|
15239
15443
|
return new ZodRecord({
|
|
15240
15444
|
type: "record",
|
|
15241
15445
|
keyType,
|
|
@@ -15281,10 +15485,10 @@ const ZodEnum = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
|
|
|
15281
15485
|
};
|
|
15282
15486
|
});
|
|
15283
15487
|
function _enum(values, params) {
|
|
15284
|
-
const
|
|
15488
|
+
const entries2 = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;
|
|
15285
15489
|
return new ZodEnum({
|
|
15286
15490
|
type: "enum",
|
|
15287
|
-
entries,
|
|
15491
|
+
entries: entries2,
|
|
15288
15492
|
...normalizeParams(params)
|
|
15289
15493
|
});
|
|
15290
15494
|
}
|
|
@@ -15469,7 +15673,7 @@ function superRefine(fn) {
|
|
|
15469
15673
|
}
|
|
15470
15674
|
function json(params) {
|
|
15471
15675
|
const jsonSchema = lazy(() => {
|
|
15472
|
-
return union([string$1(params), number(), boolean(), _null(), array$4(jsonSchema), record$
|
|
15676
|
+
return union([string$1(params), number(), boolean(), _null(), array$4(jsonSchema), record$f(string$1(), jsonSchema)]);
|
|
15473
15677
|
});
|
|
15474
15678
|
return jsonSchema;
|
|
15475
15679
|
}
|
|
@@ -17767,14 +17971,14 @@ const scriptAutomationPayloadSchema = strictObject({
|
|
|
17767
17971
|
entrypoint: entrypointSchema,
|
|
17768
17972
|
args: array$4(string$1().max(4e3)).max(64),
|
|
17769
17973
|
timeoutSeconds: number().int().min(1).max(1800),
|
|
17770
|
-
secretRefs: record$
|
|
17974
|
+
secretRefs: record$f(secretName, string$1().min(1).max(200)),
|
|
17771
17975
|
allowedOrigins: array$4(originSchema).max(100)
|
|
17772
17976
|
}),
|
|
17773
17977
|
schedule: scriptScheduleSchema.nullable(),
|
|
17774
17978
|
externalEnabled: boolean(),
|
|
17775
|
-
inputSchema: record$
|
|
17979
|
+
inputSchema: record$f(string$1(), json())
|
|
17776
17980
|
});
|
|
17777
|
-
strictObject({ input: record$
|
|
17981
|
+
strictObject({ input: record$f(string$1(), json()) });
|
|
17778
17982
|
const scriptAdmissionSchema = scriptAutomationPayloadSchema.pick({
|
|
17779
17983
|
schedule: true,
|
|
17780
17984
|
externalEnabled: true,
|
|
@@ -17801,7 +18005,7 @@ discriminatedUnion("operation", [
|
|
|
17801
18005
|
strictObject({ operation: literal("upsert"), registration: scriptRegistrationRequestSchema }),
|
|
17802
18006
|
strictObject({ operation: literal("runs"), automationId: managementId, runId: managementId.optional() }),
|
|
17803
18007
|
strictObject({ operation: literal("artifact"), artifactId: managementId }),
|
|
17804
|
-
strictObject({ operation: literal("run"), automationId: managementId, input: record$
|
|
18008
|
+
strictObject({ operation: literal("run"), automationId: managementId, input: record$f(string$1(), json()), idempotencyKey: managementId }),
|
|
17805
18009
|
strictObject({ operation: literal("cancel"), automationId: managementId, runId: managementId })
|
|
17806
18010
|
]);
|
|
17807
18011
|
const scriptAutomationRowSchema = object$3({
|
|
@@ -18007,10 +18211,10 @@ function parsePasswordLoginResponse(raw) {
|
|
|
18007
18211
|
}
|
|
18008
18212
|
function parseKdfParams(value2) {
|
|
18009
18213
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return void 0;
|
|
18010
|
-
const
|
|
18011
|
-
if (
|
|
18214
|
+
const entries2 = Object.entries(value2);
|
|
18215
|
+
if (entries2.length > 8) return void 0;
|
|
18012
18216
|
const parsed2 = {};
|
|
18013
|
-
for (const [key, entry] of
|
|
18217
|
+
for (const [key, entry] of entries2) {
|
|
18014
18218
|
if (!/^[A-Za-z][A-Za-z0-9_-]{0,63}$/.test(key)) return void 0;
|
|
18015
18219
|
if (typeof entry === "string") {
|
|
18016
18220
|
if (entry.length > 128) return void 0;
|
|
@@ -18092,14 +18296,14 @@ function parseCloudLoginResult(raw) {
|
|
|
18092
18296
|
}
|
|
18093
18297
|
const HANDOFF_ID_PATTERN = /^mph-[A-Za-z0-9_-]{22}$/;
|
|
18094
18298
|
const CAPABILITY_PATTERN = /^[A-Za-z0-9_-]{43}$/;
|
|
18095
|
-
function record$
|
|
18299
|
+
function record$e(value2) {
|
|
18096
18300
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
18097
18301
|
}
|
|
18098
18302
|
function validExpiry$1(value2) {
|
|
18099
18303
|
return typeof value2 === "number" && Number.isSafeInteger(value2) && value2 > 0;
|
|
18100
18304
|
}
|
|
18101
18305
|
function parseMfaPasskeyHandoff(raw, apiBaseUrl) {
|
|
18102
|
-
const value2 = record$
|
|
18306
|
+
const value2 = record$e(raw);
|
|
18103
18307
|
if (!value2 || typeof value2.handoffId !== "string" || !HANDOFF_ID_PATTERN.test(value2.handoffId) || typeof value2.exchangeToken !== "string" || !CAPABILITY_PATTERN.test(value2.exchangeToken) || typeof value2.authorizationUrl !== "string" || !validExpiry$1(value2.expiresAt)) {
|
|
18104
18308
|
throw new Error("MFA_PASSKEY_HANDOFF_RESPONSE_INVALID");
|
|
18105
18309
|
}
|
|
@@ -18123,7 +18327,7 @@ function parseMfaPasskeyHandoff(raw, apiBaseUrl) {
|
|
|
18123
18327
|
};
|
|
18124
18328
|
}
|
|
18125
18329
|
function parsePendingMfaPasskeyExchange(raw) {
|
|
18126
|
-
const value2 = record$
|
|
18330
|
+
const value2 = record$e(raw);
|
|
18127
18331
|
if (!value2 || value2.status !== "pending" || !validExpiry$1(value2.expiresAt)) {
|
|
18128
18332
|
throw new Error("MFA_PASSKEY_HANDOFF_RESPONSE_INVALID");
|
|
18129
18333
|
}
|
|
@@ -18325,7 +18529,7 @@ class SessionWriteLockedError extends Error {
|
|
|
18325
18529
|
this.name = "SessionWriteLockedError";
|
|
18326
18530
|
}
|
|
18327
18531
|
}
|
|
18328
|
-
const MAX_ENTRIES = 3e3;
|
|
18532
|
+
const MAX_ENTRIES$1 = 3e3;
|
|
18329
18533
|
const headerCache = /* @__PURE__ */ new Map();
|
|
18330
18534
|
function cacheGet(store, sessionId, ciphertext) {
|
|
18331
18535
|
return store.get(sessionId)?.get(ciphertext) ?? null;
|
|
@@ -18337,7 +18541,7 @@ function cacheSet(store, sessionId, ciphertext, plaintext) {
|
|
|
18337
18541
|
store.set(sessionId, perSession);
|
|
18338
18542
|
}
|
|
18339
18543
|
perSession.set(ciphertext, plaintext);
|
|
18340
|
-
if (perSession.size > MAX_ENTRIES) {
|
|
18544
|
+
if (perSession.size > MAX_ENTRIES$1) {
|
|
18341
18545
|
const oldest = perSession.keys().next().value;
|
|
18342
18546
|
if (oldest !== void 0) perSession.delete(oldest);
|
|
18343
18547
|
}
|
|
@@ -18678,7 +18882,7 @@ function personalMachinesPath(companyId) {
|
|
|
18678
18882
|
function myMachinesPath(companyId) {
|
|
18679
18883
|
return `/api/admin/companies/${encodeURIComponent(companyId)}/my-machines`;
|
|
18680
18884
|
}
|
|
18681
|
-
function record$
|
|
18885
|
+
function record$d(value2) {
|
|
18682
18886
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : {};
|
|
18683
18887
|
}
|
|
18684
18888
|
function array$3(value2) {
|
|
@@ -18697,7 +18901,7 @@ function provider(value2) {
|
|
|
18697
18901
|
return PROVIDERS.includes(str$1(value2)) ? str$1(value2) : null;
|
|
18698
18902
|
}
|
|
18699
18903
|
function parseBundle(value2) {
|
|
18700
|
-
const row2 = record$
|
|
18904
|
+
const row2 = record$d(value2);
|
|
18701
18905
|
const id2 = str$1(row2.id);
|
|
18702
18906
|
const bundleProvider = provider(row2.provider);
|
|
18703
18907
|
if (!id2 || !bundleProvider) return null;
|
|
@@ -18716,7 +18920,7 @@ function parseBundle(value2) {
|
|
|
18716
18920
|
};
|
|
18717
18921
|
}
|
|
18718
18922
|
function parseDeployment(value2) {
|
|
18719
|
-
const row2 = record$
|
|
18923
|
+
const row2 = record$d(value2);
|
|
18720
18924
|
const machineId = str$1(row2.machineId);
|
|
18721
18925
|
const bundleId = str$1(row2.bundleId);
|
|
18722
18926
|
if (!machineId || !bundleId) return null;
|
|
@@ -18736,10 +18940,10 @@ function parseDeployment(value2) {
|
|
|
18736
18940
|
};
|
|
18737
18941
|
}
|
|
18738
18942
|
function parseStatus(value2) {
|
|
18739
|
-
const row2 = record$
|
|
18943
|
+
const row2 = record$d(value2);
|
|
18740
18944
|
const statusProvider = provider(row2.provider);
|
|
18741
18945
|
if (!statusProvider) return void 0;
|
|
18742
|
-
const rotationRow = record$
|
|
18946
|
+
const rotationRow = record$d(row2.rotation);
|
|
18743
18947
|
const state = str$1(rotationRow.state);
|
|
18744
18948
|
const warningKinds = Array.isArray(rotationRow.warningKinds) ? [...new Set(rotationRow.warningKinds.filter(
|
|
18745
18949
|
(kind) => kind === "ACCOUNT_NEEDS_REAUTH" || kind === "NO_COMPARISON"
|
|
@@ -18764,7 +18968,7 @@ function parseStatus(value2) {
|
|
|
18764
18968
|
};
|
|
18765
18969
|
}
|
|
18766
18970
|
function parseMachineResult(value2) {
|
|
18767
|
-
const row2 = record$
|
|
18971
|
+
const row2 = record$d(value2);
|
|
18768
18972
|
const machineId = str$1(row2.machineId);
|
|
18769
18973
|
if (!machineId) return null;
|
|
18770
18974
|
if (row2.ok !== true) {
|
|
@@ -18779,28 +18983,28 @@ function parseMachineResult(value2) {
|
|
|
18779
18983
|
};
|
|
18780
18984
|
}
|
|
18781
18985
|
function parseAiCredentialVaultSnapshot(value2) {
|
|
18782
|
-
const body = record$
|
|
18986
|
+
const body = record$d(value2);
|
|
18783
18987
|
return {
|
|
18784
18988
|
bundles: array$3(body.bundles).map(parseBundle).filter((bundle) => bundle !== null),
|
|
18785
18989
|
deployments: array$3(body.deployments).map(parseDeployment).filter((deployment) => deployment !== null)
|
|
18786
18990
|
};
|
|
18787
18991
|
}
|
|
18788
18992
|
function parseAiCredentialCaptureResponse(value2) {
|
|
18789
|
-
const body = record$
|
|
18993
|
+
const body = record$d(value2);
|
|
18790
18994
|
const bundle = parseBundle(body.bundle);
|
|
18791
18995
|
if (!bundle) throw new Error("AI credential capture response did not contain a bundle");
|
|
18792
18996
|
return { bundle, warnings: array$3(body.warnings).filter((warning) => typeof warning === "string") };
|
|
18793
18997
|
}
|
|
18794
18998
|
function parseAiCredentialBatchResult(value2) {
|
|
18795
|
-
const body = record$
|
|
18999
|
+
const body = record$d(value2);
|
|
18796
19000
|
return {
|
|
18797
19001
|
results: array$3(body.results).map(parseMachineResult).filter((result2) => result2 !== null),
|
|
18798
19002
|
warnings: array$3(body.warnings).filter((warning) => typeof warning === "string")
|
|
18799
19003
|
};
|
|
18800
19004
|
}
|
|
18801
19005
|
function parseSelfServiceMachines(value2) {
|
|
18802
|
-
return array$3(record$
|
|
18803
|
-
const row2 = record$
|
|
19006
|
+
return array$3(record$d(value2).machines).map((entry) => {
|
|
19007
|
+
const row2 = record$d(entry);
|
|
18804
19008
|
return {
|
|
18805
19009
|
machineId: str$1(row2.machineId),
|
|
18806
19010
|
type: row2.type === "shared" ? "shared" : "personal",
|
|
@@ -18813,8 +19017,8 @@ function parseSelfServiceMachines(value2) {
|
|
|
18813
19017
|
}).filter((machine) => machine.machineId !== "");
|
|
18814
19018
|
}
|
|
18815
19019
|
function parsePersonalMachines(value2) {
|
|
18816
|
-
return array$3(record$
|
|
18817
|
-
const row2 = record$
|
|
19020
|
+
return array$3(record$d(value2).machines).map((entry) => {
|
|
19021
|
+
const row2 = record$d(entry);
|
|
18818
19022
|
const type = row2.type === "shared" ? "shared" : "personal";
|
|
18819
19023
|
return {
|
|
18820
19024
|
machineId: str$1(row2.machineId),
|
|
@@ -18849,7 +19053,7 @@ const orgSearchPaths = {
|
|
|
18849
19053
|
cursors: (companyId, projectId) => `/api/companies/${encodeURIComponent(companyId)}/search-index/cursors${projectId === void 0 ? "" : `?projectId=${encodeURIComponent(projectId)}`}`,
|
|
18850
19054
|
progress: (companyId) => `/api/companies/${encodeURIComponent(companyId)}/search-index/progress`
|
|
18851
19055
|
};
|
|
18852
|
-
function record$
|
|
19056
|
+
function record$c(value2) {
|
|
18853
19057
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : {};
|
|
18854
19058
|
}
|
|
18855
19059
|
function str(value2, fallback = "") {
|
|
@@ -18860,9 +19064,9 @@ function num(value2, fallback = 0) {
|
|
|
18860
19064
|
}
|
|
18861
19065
|
const PHASES = ["idle", "waiting", "indexing", "done"];
|
|
18862
19066
|
function parseOrgSearchCursors(raw) {
|
|
18863
|
-
const list2 = Array.isArray(record$
|
|
19067
|
+
const list2 = Array.isArray(record$c(raw).cursors) ? record$c(raw).cursors : [];
|
|
18864
19068
|
return list2.flatMap((item) => {
|
|
18865
|
-
const c = record$
|
|
19069
|
+
const c = record$c(item);
|
|
18866
19070
|
if (typeof c.sessionId !== "string" || typeof c.lastSeq !== "number") return [];
|
|
18867
19071
|
return [{
|
|
18868
19072
|
sessionId: c.sessionId,
|
|
@@ -18874,7 +19078,7 @@ function parseOrgSearchCursors(raw) {
|
|
|
18874
19078
|
});
|
|
18875
19079
|
}
|
|
18876
19080
|
function parseProjectProgress(raw) {
|
|
18877
|
-
const p = record$
|
|
19081
|
+
const p = record$c(raw);
|
|
18878
19082
|
return {
|
|
18879
19083
|
projectId: str(p.projectId),
|
|
18880
19084
|
sessionsTotal: num(p.sessionsTotal),
|
|
@@ -18887,7 +19091,7 @@ function parseProjectProgress(raw) {
|
|
|
18887
19091
|
};
|
|
18888
19092
|
}
|
|
18889
19093
|
function parseOrgSearchProgress(raw) {
|
|
18890
|
-
const r = record$
|
|
19094
|
+
const r = record$c(raw);
|
|
18891
19095
|
return {
|
|
18892
19096
|
phase: PHASES.includes(r.phase) ? r.phase : "idle",
|
|
18893
19097
|
sessionsTotal: num(r.sessionsTotal),
|
|
@@ -19364,7 +19568,7 @@ const PUBLICATION_KEYS = /* @__PURE__ */ new Set([
|
|
|
19364
19568
|
function fail$9(detail) {
|
|
19365
19569
|
throw new Error(`artifact publication response is invalid: ${detail}`);
|
|
19366
19570
|
}
|
|
19367
|
-
function record$
|
|
19571
|
+
function record$b(value2, label) {
|
|
19368
19572
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) fail$9(`${label} must be an object`);
|
|
19369
19573
|
return value2;
|
|
19370
19574
|
}
|
|
@@ -19410,7 +19614,7 @@ function sourceIdentity(source2) {
|
|
|
19410
19614
|
]) : JSON.stringify([source2.chatId, source2.path]);
|
|
19411
19615
|
}
|
|
19412
19616
|
function validatePublication(value2, apiBaseUrl, operation, expectedSource) {
|
|
19413
|
-
const publication = record$
|
|
19617
|
+
const publication = record$b(value2, "publication");
|
|
19414
19618
|
if (Object.keys(publication).some((key) => !PUBLICATION_KEYS.has(key))) {
|
|
19415
19619
|
fail$9("publication contains an unexpected field");
|
|
19416
19620
|
}
|
|
@@ -19443,7 +19647,7 @@ function validatePublication(value2, apiBaseUrl, operation, expectedSource) {
|
|
|
19443
19647
|
};
|
|
19444
19648
|
}
|
|
19445
19649
|
function parseStaticArtifactPublicationResult(value2, options) {
|
|
19446
|
-
const response2 = record$
|
|
19650
|
+
const response2 = record$b(value2, "response");
|
|
19447
19651
|
if (options.operation === "publish") {
|
|
19448
19652
|
exactKeys$3(response2, ["publication", "publicUrl", "reused"]);
|
|
19449
19653
|
if (typeof response2.reused !== "boolean") fail$9("reused is invalid");
|
|
@@ -19497,6 +19701,60 @@ function parseLessonCandidate(value2) {
|
|
|
19497
19701
|
}
|
|
19498
19702
|
};
|
|
19499
19703
|
}
|
|
19704
|
+
const MAX_ENTRIES = 1e4;
|
|
19705
|
+
const entries = /* @__PURE__ */ new Map();
|
|
19706
|
+
const MAX_KEY_SETS = 8;
|
|
19707
|
+
let keySets = [];
|
|
19708
|
+
function touchKeySet(set) {
|
|
19709
|
+
const index = keySets.indexOf(set);
|
|
19710
|
+
if (index >= 0 && index < keySets.length - 1) keySets.push(...keySets.splice(index, 1));
|
|
19711
|
+
}
|
|
19712
|
+
function sharedKeyCopy(keys2) {
|
|
19713
|
+
const existing = keySets.find((set) => sameKeyBytes(set, keys2));
|
|
19714
|
+
if (existing) {
|
|
19715
|
+
touchKeySet(existing);
|
|
19716
|
+
return existing;
|
|
19717
|
+
}
|
|
19718
|
+
const copy = keys2.map((key) => Uint8Array.from(key));
|
|
19719
|
+
keySets.push(copy);
|
|
19720
|
+
const evicted = keySets.length > MAX_KEY_SETS ? keySets.shift() : void 0;
|
|
19721
|
+
if (evicted) {
|
|
19722
|
+
for (const [id2, entry] of entries) if (entry.keys === evicted) entries.delete(id2);
|
|
19723
|
+
for (const key of evicted) key.fill(0);
|
|
19724
|
+
}
|
|
19725
|
+
return copy;
|
|
19726
|
+
}
|
|
19727
|
+
function sameKeyBytes(a, b) {
|
|
19728
|
+
if (a.length !== b.length) return false;
|
|
19729
|
+
return a.every((key, index) => {
|
|
19730
|
+
const other = b[index];
|
|
19731
|
+
if (key === other) return true;
|
|
19732
|
+
if (key.length !== other.length) return false;
|
|
19733
|
+
for (let i = 0; i < key.length; i += 1) if (key[i] !== other[i]) return false;
|
|
19734
|
+
return true;
|
|
19735
|
+
});
|
|
19736
|
+
}
|
|
19737
|
+
function memoizeSessionHeaders(api, keys2, decode2) {
|
|
19738
|
+
const agentStateBlob = api.agentState || null;
|
|
19739
|
+
const cached2 = entries.get(api.id);
|
|
19740
|
+
if (cached2 && cached2.metadata === api.metadata && cached2.agentState === agentStateBlob && sameKeyBytes(cached2.keys, keys2) && cached2.dataKeyBlobs.every((blob) => getCachedDataKeyMetadataJson(api.id, blob) !== null)) {
|
|
19741
|
+
entries.delete(api.id);
|
|
19742
|
+
entries.set(api.id, cached2);
|
|
19743
|
+
touchKeySet(cached2.keys);
|
|
19744
|
+
return cached2.headers;
|
|
19745
|
+
}
|
|
19746
|
+
const headers2 = decode2();
|
|
19747
|
+
entries.delete(api.id);
|
|
19748
|
+
if (headers2.metadata !== null && (agentStateBlob === null || headers2.agentState !== null)) {
|
|
19749
|
+
const dataKeyBlobs = [api.metadata, agentStateBlob].filter((blob) => !!blob && getCachedDataKeyMetadataJson(api.id, blob) !== null);
|
|
19750
|
+
entries.set(api.id, { metadata: api.metadata, agentState: agentStateBlob, keys: sharedKeyCopy(keys2), dataKeyBlobs, headers: headers2 });
|
|
19751
|
+
if (entries.size > MAX_ENTRIES) {
|
|
19752
|
+
const oldest = entries.keys().next().value;
|
|
19753
|
+
if (oldest !== void 0) entries.delete(oldest);
|
|
19754
|
+
}
|
|
19755
|
+
}
|
|
19756
|
+
return headers2;
|
|
19757
|
+
}
|
|
19500
19758
|
function asRecord(value2) {
|
|
19501
19759
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
19502
19760
|
}
|
|
@@ -28326,6 +28584,12 @@ function sessionTitleFromMetadata(metadata) {
|
|
|
28326
28584
|
const text2 = summary.text;
|
|
28327
28585
|
return typeof text2 === "string" && text2.trim() ? text2 : void 0;
|
|
28328
28586
|
}
|
|
28587
|
+
function summaryTextFromMetadata(metadata) {
|
|
28588
|
+
const summary = metadata?.summary;
|
|
28589
|
+
if (!summary || typeof summary !== "object" || Array.isArray(summary)) return void 0;
|
|
28590
|
+
const text2 = summary.text;
|
|
28591
|
+
return typeof text2 === "string" && text2.trim() ? text2 : void 0;
|
|
28592
|
+
}
|
|
28329
28593
|
function branchSlugFromMetadata(metadata) {
|
|
28330
28594
|
const summary = metadata?.summary;
|
|
28331
28595
|
if (!summary || typeof summary !== "object" || Array.isArray(summary)) return void 0;
|
|
@@ -28731,8 +28995,8 @@ function nonEmptyString$1(value2) {
|
|
|
28731
28995
|
}
|
|
28732
28996
|
function coerceStringArray(value2) {
|
|
28733
28997
|
if (!Array.isArray(value2)) return void 0;
|
|
28734
|
-
const
|
|
28735
|
-
return
|
|
28998
|
+
const entries2 = value2.filter((entry) => typeof entry === "string" && entry.trim() !== "");
|
|
28999
|
+
return entries2.length > 0 ? entries2 : void 0;
|
|
28736
29000
|
}
|
|
28737
29001
|
function optionalTimestamp(value2) {
|
|
28738
29002
|
if (typeof value2 !== "string" && typeof value2 !== "number" && !(value2 instanceof Date) && value2 !== null && value2 !== void 0) {
|
|
@@ -28762,7 +29026,7 @@ const MCP_RUNTIME_STATUSES = /* @__PURE__ */ new Set([
|
|
|
28762
29026
|
]);
|
|
28763
29027
|
function coerceMcpServerStatuses(value2) {
|
|
28764
29028
|
if (!Array.isArray(value2)) return void 0;
|
|
28765
|
-
const
|
|
29029
|
+
const entries2 = [];
|
|
28766
29030
|
for (const raw of value2) {
|
|
28767
29031
|
if (!isRecord$3(raw)) continue;
|
|
28768
29032
|
const name = nonEmptyString$1(raw.name);
|
|
@@ -28770,9 +29034,9 @@ function coerceMcpServerStatuses(value2) {
|
|
|
28770
29034
|
const checkedAt = optionalTimestamp(raw.checkedAt);
|
|
28771
29035
|
if (!name || !status || checkedAt === void 0) continue;
|
|
28772
29036
|
const error = nonEmptyString$1(raw.error);
|
|
28773
|
-
|
|
29037
|
+
entries2.push({ name, status, checkedAt, ...error ? { error } : {} });
|
|
28774
29038
|
}
|
|
28775
|
-
return
|
|
29039
|
+
return entries2.length > 0 ? entries2 : void 0;
|
|
28776
29040
|
}
|
|
28777
29041
|
function coerceCreatedBy(value2) {
|
|
28778
29042
|
if (!isRecord$3(value2)) return void 0;
|
|
@@ -28794,16 +29058,20 @@ function decryptSessionJsonSync(sessionId, blob, candidateKeys) {
|
|
|
28794
29058
|
return decryptJsonWithCandidates(blob, candidateKeys);
|
|
28795
29059
|
}
|
|
28796
29060
|
function apiSessionToSession(api, candidateKeys, existing) {
|
|
28797
|
-
const
|
|
29061
|
+
const decoded = memoizeSessionHeaders(api, candidateKeys, () => ({
|
|
29062
|
+
metadata: decryptSessionJsonSync(api.id, api.metadata, candidateKeys),
|
|
29063
|
+
summaryText: decryptSummaryText(api.metadata, candidateKeys),
|
|
29064
|
+
remoteThinking: apiSessionAgentStateThinking(api.agentState, candidateKeys),
|
|
29065
|
+
pendingAskUserQuestion: apiSessionAgentStatePendingAskUserQuestion(api.agentState, candidateKeys),
|
|
29066
|
+
pendingExitPlanMode: apiSessionAgentStatePendingExitPlanMode(api.agentState, candidateKeys),
|
|
29067
|
+
pendingPermissionRequests: apiSessionAgentStatePendingPermissions(api.agentState, candidateKeys),
|
|
29068
|
+
agentControlledByUser: apiSessionAgentStateControlledByUser(api.agentState, candidateKeys),
|
|
29069
|
+
agentState: api.agentState ? decryptSessionJsonSync(api.id, api.agentState, candidateKeys) : null
|
|
29070
|
+
}));
|
|
29071
|
+
const { metadata, remoteThinking, pendingAskUserQuestion, pendingExitPlanMode, pendingPermissionRequests, agentControlledByUser, agentState } = decoded;
|
|
28798
29072
|
const updatedAt = mergeUpdatedAt(api.updatedAt);
|
|
28799
|
-
const summaryText =
|
|
29073
|
+
const summaryText = summaryTextFromMetadata(metadata) ?? decoded.summaryText ?? existing?.summaryText;
|
|
28800
29074
|
const machineId = nonEmptyString$1(metadata?.machineId) ?? existing?.machineId;
|
|
28801
|
-
const remoteThinking = apiSessionAgentStateThinking(api.agentState, candidateKeys);
|
|
28802
|
-
const pendingAskUserQuestion = apiSessionAgentStatePendingAskUserQuestion(api.agentState, candidateKeys);
|
|
28803
|
-
const pendingExitPlanMode = apiSessionAgentStatePendingExitPlanMode(api.agentState, candidateKeys);
|
|
28804
|
-
const pendingPermissionRequests = apiSessionAgentStatePendingPermissions(api.agentState, candidateKeys);
|
|
28805
|
-
const agentControlledByUser = apiSessionAgentStateControlledByUser(api.agentState, candidateKeys);
|
|
28806
|
-
const agentState = api.agentState ? decryptSessionJsonSync(api.id, api.agentState, candidateKeys) : null;
|
|
28807
29075
|
const agent = agentFromFlavor(typeof metadata?.flavor === "string" ? metadata.flavor : void 0) ?? existing?.agent;
|
|
28808
29076
|
const models = coerceSessionModels(metadata?.models) ?? existing?.models;
|
|
28809
29077
|
const worktree = coerceSessionWorktree(metadata?.worktree) ?? coerceSessionWorktree(metadata) ?? existing?.worktree;
|
|
@@ -28928,7 +29196,7 @@ async function prepareMcpCallerGrantEnvelope(input, deps) {
|
|
|
28928
29196
|
return { ok: false, reason: "invalid-key" };
|
|
28929
29197
|
}
|
|
28930
29198
|
}
|
|
28931
|
-
function record$
|
|
29199
|
+
function record$a(value2) {
|
|
28932
29200
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
28933
29201
|
}
|
|
28934
29202
|
function text$7(value2) {
|
|
@@ -28943,7 +29211,7 @@ function timestamp(value2, fallback) {
|
|
|
28943
29211
|
return fallback;
|
|
28944
29212
|
}
|
|
28945
29213
|
function parseInvite(value2) {
|
|
28946
|
-
const row2 = record$
|
|
29214
|
+
const row2 = record$a(value2);
|
|
28947
29215
|
if (!row2) return null;
|
|
28948
29216
|
const inviteId = text$7(row2.inviteId ?? row2.id);
|
|
28949
29217
|
const companyId = text$7(row2.companyId);
|
|
@@ -28963,7 +29231,7 @@ function parseInvite(value2) {
|
|
|
28963
29231
|
};
|
|
28964
29232
|
}
|
|
28965
29233
|
function parsePendingCompanyInvites(value2) {
|
|
28966
|
-
const rows = Array.isArray(value2) ? value2 : record$
|
|
29234
|
+
const rows = Array.isArray(value2) ? value2 : record$a(value2)?.invites;
|
|
28967
29235
|
if (!Array.isArray(rows)) throw new Error("Invalid invitation response");
|
|
28968
29236
|
const seen = /* @__PURE__ */ new Set();
|
|
28969
29237
|
const result2 = [];
|
|
@@ -29067,9 +29335,9 @@ const STANDALONE_USER_ID = "standalone-local";
|
|
|
29067
29335
|
function isStandaloneAuthSession(session) {
|
|
29068
29336
|
return session?.userId === STANDALONE_USER_ID;
|
|
29069
29337
|
}
|
|
29070
|
-
function indexMachineRegistry(
|
|
29338
|
+
function indexMachineRegistry(entries2) {
|
|
29071
29339
|
const byId = {};
|
|
29072
|
-
for (const entry of
|
|
29340
|
+
for (const entry of entries2) {
|
|
29073
29341
|
if (!entry?.machineId) continue;
|
|
29074
29342
|
const { machineId, ...info } = entry;
|
|
29075
29343
|
byId[machineId] = info;
|
|
@@ -29176,7 +29444,7 @@ const TERMINAL_CODES = /* @__PURE__ */ new Set([
|
|
|
29176
29444
|
function invalid$1() {
|
|
29177
29445
|
throw new Error("session-followup-response-invalid");
|
|
29178
29446
|
}
|
|
29179
|
-
function record$
|
|
29447
|
+
function record$9(value2) {
|
|
29180
29448
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) invalid$1();
|
|
29181
29449
|
return value2;
|
|
29182
29450
|
}
|
|
@@ -29210,7 +29478,7 @@ function validateSessionFollowupCreateInput(input) {
|
|
|
29210
29478
|
}
|
|
29211
29479
|
function parseSessionFollowupPublic(value2) {
|
|
29212
29480
|
try {
|
|
29213
|
-
const item = record$
|
|
29481
|
+
const item = record$9(value2);
|
|
29214
29482
|
if (!STATUSES$1.has(item.status)) invalid$1();
|
|
29215
29483
|
if (item.terminalCode !== null && !TERMINAL_CODES.has(item.terminalCode)) invalid$1();
|
|
29216
29484
|
if (item.payloadVersion !== 1) invalid$1();
|
|
@@ -29481,9 +29749,9 @@ function rememberRows(scope2, requestedIds, rows, now) {
|
|
|
29481
29749
|
for (const id2 of requestedIds) cache2.delete(id2);
|
|
29482
29750
|
for (const entry of rows) cache2.set(entry.row.id, entry);
|
|
29483
29751
|
const idleBefore = now - SESSION_ROW_CACHE_IDLE_MS;
|
|
29484
|
-
for (const [otherScope,
|
|
29485
|
-
for (const [id2, entry] of
|
|
29486
|
-
if (
|
|
29752
|
+
for (const [otherScope, entries2] of rowsByScope) {
|
|
29753
|
+
for (const [id2, entry] of entries2) if (entry.seenAt <= idleBefore) entries2.delete(id2);
|
|
29754
|
+
if (entries2.size === 0) rowsByScope.delete(otherScope);
|
|
29487
29755
|
}
|
|
29488
29756
|
for (const [otherScope, until] of versionUnsupportedUntilByScope) {
|
|
29489
29757
|
if (until <= now) versionUnsupportedUntilByScope.delete(otherScope);
|
|
@@ -29626,7 +29894,8 @@ function normalizeOptionalIsoDate(value2) {
|
|
|
29626
29894
|
function normalizeGithubPatStatus(raw) {
|
|
29627
29895
|
return {
|
|
29628
29896
|
hasPat: raw.hasPat ?? raw.registered ?? false,
|
|
29629
|
-
updatedAt: normalizeOptionalIsoDate(raw.updatedAt)
|
|
29897
|
+
updatedAt: normalizeOptionalIsoDate(raw.updatedAt),
|
|
29898
|
+
...raw.provider === "github" || raw.provider === "gitlab" ? { provider: raw.provider } : {}
|
|
29630
29899
|
};
|
|
29631
29900
|
}
|
|
29632
29901
|
function normalizeGitProviderPage(provider2, rawRepositories, hasMore) {
|
|
@@ -30151,7 +30420,18 @@ class ApiClient {
|
|
|
30151
30420
|
}
|
|
30152
30421
|
async getCompanies() {
|
|
30153
30422
|
const data = await this.request("/api/companies/me", { boundary: "api" });
|
|
30154
|
-
|
|
30423
|
+
const companies = Array.isArray(data) ? data : data?.companies;
|
|
30424
|
+
if (!Array.isArray(companies) || companies.some((row2) => !row2 || typeof row2.id !== "string" || !row2.id.trim() || typeof row2.slug !== "string" || !row2.slug.trim() || typeof row2.displayName !== "string" || !row2.displayName.trim() || !["owner", "admin", "member"].includes(row2.myRole))) throw new Error("Invalid company membership response");
|
|
30425
|
+
return companies;
|
|
30426
|
+
}
|
|
30427
|
+
companyEnrollmentApi() {
|
|
30428
|
+
return createCompanyEnrollmentApi((path2, init) => this.request(path2, { ...init, boundary: "api" }));
|
|
30429
|
+
}
|
|
30430
|
+
joinCompanyByCode(code, mfaSessionToken) {
|
|
30431
|
+
return this.companyEnrollmentApi().joinCompanyByCode(code, mfaSessionToken);
|
|
30432
|
+
}
|
|
30433
|
+
createCompany(input) {
|
|
30434
|
+
return this.companyEnrollmentApi().createCompany(input);
|
|
30155
30435
|
}
|
|
30156
30436
|
async getPendingCompanyInvites() {
|
|
30157
30437
|
const invites = [], cursors = /* @__PURE__ */ new Set();
|
|
@@ -30458,6 +30738,9 @@ class ApiClient {
|
|
|
30458
30738
|
async startGithubIntegrationConnect() {
|
|
30459
30739
|
return this.request("/api/integrations/github/connect-url", { method: "POST", boundary: "api" });
|
|
30460
30740
|
}
|
|
30741
|
+
async startGithubIntegrationInstallation() {
|
|
30742
|
+
return this.request("/api/integrations/github/install-url", { method: "POST", boundary: "api" });
|
|
30743
|
+
}
|
|
30461
30744
|
async searchGitLabIntegrationProjects(search, page = 1) {
|
|
30462
30745
|
const params = new URLSearchParams({ search, page: String(page) });
|
|
30463
30746
|
const data = await this.request(
|
|
@@ -31602,6 +31885,12 @@ class ApiClient {
|
|
|
31602
31885
|
if (list2.length === 0) return null;
|
|
31603
31886
|
return list2.find((pr) => pr.state === "open") ?? list2.find((pr) => pr.state === "merged") ?? list2[0];
|
|
31604
31887
|
}
|
|
31888
|
+
/** specs/pr-follow-through R3-2 — 머지 가드가 머지와 같은 권위 경로로 열린 PR 을 본다. */
|
|
31889
|
+
async listOpenGithubPullRequests(projectId) {
|
|
31890
|
+
const params = new URLSearchParams({ state: "open", perPage: "100" });
|
|
31891
|
+
const raw = await this.request(`/api/projects/${encodeURIComponent(projectId)}/pull-requests?${params.toString()}`, { boundary: "api" });
|
|
31892
|
+
return extractPullRequestList(raw).map((entry) => normalizeGithubPullRequest(entry));
|
|
31893
|
+
}
|
|
31605
31894
|
async mergeGithubPullRequest(input, tokenOverride) {
|
|
31606
31895
|
const raw = await this.request(
|
|
31607
31896
|
`/api/projects/${encodeURIComponent(input.projectId)}/pull-requests/${encodeURIComponent(String(input.number))}/merge`,
|
|
@@ -32441,12 +32730,22 @@ function parseAccessKeyFile(raw) {
|
|
|
32441
32730
|
}
|
|
32442
32731
|
if (!parsed2 || typeof parsed2 !== "object") return null;
|
|
32443
32732
|
const { token, secret } = parsed2;
|
|
32444
|
-
if (!isNonEmptyString$2(token)
|
|
32445
|
-
|
|
32733
|
+
if (!isNonEmptyString$2(token)) return null;
|
|
32734
|
+
let keys2;
|
|
32735
|
+
if ("secret" in parsed2) {
|
|
32736
|
+
if (!isNonEmptyString$2(secret) || decodedByteLength(secret) !== SECRETBOX_KEY_BYTES) return null;
|
|
32737
|
+
keys2 = { secret };
|
|
32738
|
+
} else {
|
|
32739
|
+
const encryption = parsed2.encryption;
|
|
32740
|
+
if (!encryption || typeof encryption !== "object") return null;
|
|
32741
|
+
const { publicKey, machineKey } = encryption;
|
|
32742
|
+
if (!isNonEmptyString$2(publicKey) || decodedByteLength(publicKey) !== 32 || !isNonEmptyString$2(machineKey) || decodedByteLength(machineKey) !== 32) return null;
|
|
32743
|
+
keys2 = { encryption: { publicKey, machineKey } };
|
|
32744
|
+
}
|
|
32446
32745
|
const { apiToken, serverUrl } = parsed2;
|
|
32447
32746
|
return {
|
|
32448
32747
|
token,
|
|
32449
|
-
|
|
32748
|
+
...keys2,
|
|
32450
32749
|
...isNonEmptyString$2(apiToken) ? { apiToken } : {},
|
|
32451
32750
|
...isOriginUrl(serverUrl) ? { serverUrl } : {}
|
|
32452
32751
|
};
|
|
@@ -32513,6 +32812,10 @@ function missingCredentialMessage() {
|
|
|
32513
32812
|
"Set SAYCODE_HAPPY_HOME to point at a different Happy home directory."
|
|
32514
32813
|
].join("\n");
|
|
32515
32814
|
}
|
|
32815
|
+
function requireLegacySecret(credential) {
|
|
32816
|
+
if (!credential.secret) throw new Error("This operation requires a legacy secret; dataKey credentials need a scoped encryption key.");
|
|
32817
|
+
return credential.secret;
|
|
32818
|
+
}
|
|
32516
32819
|
const PERSONAL_WORKSPACE_ID = "personal";
|
|
32517
32820
|
const PERSONAL_WORKSPACE = { id: PERSONAL_WORKSPACE_ID, name: "Personal", kind: "personal" };
|
|
32518
32821
|
function text$6(value2) {
|
|
@@ -32707,7 +33010,8 @@ class RealtimeSocket {
|
|
|
32707
33010
|
return new Error("Realtime WebSocket connection was cancelled because the transport was disposed.");
|
|
32708
33011
|
}
|
|
32709
33012
|
safeErrorMessage(error) {
|
|
32710
|
-
|
|
33013
|
+
const secrets = [this.credential.token, this.credential.secret, this.credential.encryption?.machineKey];
|
|
33014
|
+
return secrets.reduce((message, secret) => secret ? message.replaceAll(secret, "[redacted]") : message, error.message);
|
|
32711
33015
|
}
|
|
32712
33016
|
on(event, listener) {
|
|
32713
33017
|
this.socket.on(event, listener);
|
|
@@ -32742,13 +33046,13 @@ class CliTransport {
|
|
|
32742
33046
|
constructor(credential, serverUrl = DEFAULT_SERVER_URL) {
|
|
32743
33047
|
this.credential = credential;
|
|
32744
33048
|
this.serverUrl = serverUrl;
|
|
32745
|
-
this.secretKey = decodeBase64$1(credential.secret);
|
|
33049
|
+
this.secretKey = credential.secret ? decodeBase64$1(credential.secret) : null;
|
|
32746
33050
|
this.realtime = new RealtimeSocket(credential, serverUrl);
|
|
32747
33051
|
}
|
|
32748
33052
|
realtime;
|
|
32749
33053
|
secretKey;
|
|
32750
33054
|
get key() {
|
|
32751
|
-
return this.secretKey;
|
|
33055
|
+
return this.secretKey ?? decodeBase64$1(requireLegacySecret(this.credential));
|
|
32752
33056
|
}
|
|
32753
33057
|
connect(timeoutMs = DEFAULT_CONNECT_TIMEOUT_MS) {
|
|
32754
33058
|
return this.realtime.connect(timeoutMs);
|
|
@@ -32764,21 +33068,28 @@ class CliTransport {
|
|
|
32764
33068
|
* `callDesktopMachineRpc` (src/sync/sessionCreation.ts). `target` is the
|
|
32765
33069
|
* machine id for daemon methods and the session id for session methods.
|
|
32766
33070
|
*/
|
|
32767
|
-
async rpc(target, method, params, ackTimeoutMs = DEFAULT_ACK_TIMEOUT_MS) {
|
|
32768
|
-
const
|
|
33071
|
+
async rpc(target, method, params, ackTimeoutMs = DEFAULT_ACK_TIMEOUT_MS, machineKey) {
|
|
33072
|
+
const nonce = Buffer.from(crypto.getRandomValues(new Uint8Array(16))).toString("base64");
|
|
33073
|
+
const encrypted = machineKey ? await encryptDataKeyContent(machineKey, { rpcBinding: 1, method, scope: target, issuedAt: Date.now(), nonce, params }) : encryptSecretBox(JSON.stringify(params), this.key);
|
|
32769
33074
|
const response2 = await this.realtime.emitWithAck("rpc-call", {
|
|
32770
33075
|
method: `${target}:${method}`,
|
|
32771
33076
|
params: encrypted
|
|
32772
33077
|
}, ackTimeoutMs);
|
|
32773
33078
|
if (!response2.ok) throw new Error(response2.error || `RPC ${method} was rejected`);
|
|
33079
|
+
if (machineKey) {
|
|
33080
|
+
const opened = response2.result ? await decryptDataKeyJson(machineKey, response2.result) : null;
|
|
33081
|
+
if (opened?.rpcBinding !== 1 || opened.nonce !== nonce) throw new Error("MACHINE_RPC_BINDING_FAILED");
|
|
33082
|
+
const result2 = opened.result;
|
|
33083
|
+
if (result2 && typeof result2 === "object" && "error" in result2) throw new Error(String(result2.error));
|
|
33084
|
+
return result2 ?? null;
|
|
33085
|
+
}
|
|
32774
33086
|
if (!response2.result) return null;
|
|
32775
33087
|
return this.decodePayload(response2.result);
|
|
32776
33088
|
}
|
|
32777
|
-
/**
|
|
33089
|
+
/** Decode plaintext JSON or legacy secretbox; dataKey payloads require their scoped key. */
|
|
32778
33090
|
decodePayload(raw) {
|
|
32779
33091
|
if (/^[{["]/.test(raw)) return JSON.parse(raw);
|
|
32780
|
-
|
|
32781
|
-
return JSON.parse(Buffer.from(raw, "base64").toString("utf8"));
|
|
33092
|
+
return JSON.parse(decryptSecretBox(raw, this.key));
|
|
32782
33093
|
}
|
|
32783
33094
|
dispose() {
|
|
32784
33095
|
this.realtime.dispose();
|
|
@@ -33507,19 +33818,19 @@ function reduceSessionReadState(state, change, merge2, fingerprintLimit = DEFAUL
|
|
|
33507
33818
|
fingerprints: fingerprints2
|
|
33508
33819
|
};
|
|
33509
33820
|
}
|
|
33510
|
-
function record$
|
|
33821
|
+
function record$8(value2) {
|
|
33511
33822
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
33512
33823
|
}
|
|
33513
33824
|
function unwrapEncryptedField(value2) {
|
|
33514
33825
|
if (typeof value2 === "string") return value2;
|
|
33515
|
-
const envelope = record$
|
|
33826
|
+
const envelope = record$8(value2);
|
|
33516
33827
|
if (!envelope) return null;
|
|
33517
33828
|
return typeof envelope.c === "string" ? envelope.c : null;
|
|
33518
33829
|
}
|
|
33519
33830
|
function readNewMessage(update, sessionId) {
|
|
33520
|
-
const body = record$
|
|
33831
|
+
const body = record$8(record$8(update)?.body);
|
|
33521
33832
|
if (!body || body.t !== "new-message" || body.sid !== sessionId) return null;
|
|
33522
|
-
const message = record$
|
|
33833
|
+
const message = record$8(body.message);
|
|
33523
33834
|
if (!message) return null;
|
|
33524
33835
|
const ciphertext = unwrapEncryptedField(message.content);
|
|
33525
33836
|
if (ciphertext === null) return null;
|
|
@@ -33530,9 +33841,9 @@ function readNewMessage(update, sessionId) {
|
|
|
33530
33841
|
};
|
|
33531
33842
|
}
|
|
33532
33843
|
function readSessionAgentState(update, sessionId) {
|
|
33533
|
-
const body = record$
|
|
33844
|
+
const body = record$8(record$8(update)?.body);
|
|
33534
33845
|
if (!body || body.t !== "update-session" || body.id !== sessionId) return null;
|
|
33535
|
-
const agentState = record$
|
|
33846
|
+
const agentState = record$8(body.agentState);
|
|
33536
33847
|
if (!agentState) return null;
|
|
33537
33848
|
return unwrapEncryptedField(agentState.value);
|
|
33538
33849
|
}
|
|
@@ -34413,6 +34724,40 @@ function readMachineId(input, readFile2) {
|
|
|
34413
34724
|
function spawnBudgetKey(env) {
|
|
34414
34725
|
return env.agentId ?? env.root;
|
|
34415
34726
|
}
|
|
34727
|
+
function readLocalMachineRpcKey(input, machineId, readFile2) {
|
|
34728
|
+
for (const path2 of machineSettingsPaths(input)) {
|
|
34729
|
+
const raw = readFile2(path2);
|
|
34730
|
+
if (!raw) continue;
|
|
34731
|
+
let id2;
|
|
34732
|
+
try {
|
|
34733
|
+
id2 = JSON.parse(raw)?.machineId;
|
|
34734
|
+
} catch {
|
|
34735
|
+
continue;
|
|
34736
|
+
}
|
|
34737
|
+
if (typeof id2 !== "string" || !id2) continue;
|
|
34738
|
+
if (id2 !== machineId) throw new Error("MACHINE_RPC_SCOPE_MISMATCH");
|
|
34739
|
+
const credential = readFile2(path2.replace(/settings\.json$/, "access.key"));
|
|
34740
|
+
if (!credential) return null;
|
|
34741
|
+
let parsed2;
|
|
34742
|
+
try {
|
|
34743
|
+
parsed2 = JSON.parse(credential);
|
|
34744
|
+
} catch {
|
|
34745
|
+
throw new Error("MACHINE_RPC_KEY_INVALID");
|
|
34746
|
+
}
|
|
34747
|
+
if (!parsed2 || typeof parsed2 !== "object") throw new Error("MACHINE_RPC_KEY_INVALID");
|
|
34748
|
+
if ("secret" in parsed2) {
|
|
34749
|
+
if (!parseAccessKeyFile(credential)) throw new Error("MACHINE_RPC_KEY_INVALID");
|
|
34750
|
+
return null;
|
|
34751
|
+
}
|
|
34752
|
+
if (!("encryption" in parsed2)) return null;
|
|
34753
|
+
const encoded = parsed2.encryption?.machineKey;
|
|
34754
|
+
if (typeof encoded !== "string") throw new Error("MACHINE_RPC_KEY_INVALID");
|
|
34755
|
+
const key = Buffer.from(encoded, "base64");
|
|
34756
|
+
if (key.length !== 32 || key.toString("base64") !== encoded) throw new Error("MACHINE_RPC_KEY_INVALID");
|
|
34757
|
+
return new Uint8Array(key);
|
|
34758
|
+
}
|
|
34759
|
+
throw new Error("MACHINE_RPC_SCOPE_MISMATCH");
|
|
34760
|
+
}
|
|
34416
34761
|
const CLI_MODEL_CATALOG_TIMEOUT_MS = 3e3;
|
|
34417
34762
|
const CLI_MODEL_CATALOG_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
34418
34763
|
const CATALOG_FILE_MODE = 384;
|
|
@@ -34492,7 +34837,7 @@ function createCliSessionConnection(bound, deps = REAL_CLI_SESSION_CONNECTION_DE
|
|
|
34492
34837
|
model: input.model ?? bound.settings.model,
|
|
34493
34838
|
effort: input.effort === void 0 ? bound.settings.effort : input.effort
|
|
34494
34839
|
}, input.autoRoute);
|
|
34495
|
-
const key = new Uint8Array(Buffer.from(bound.credential
|
|
34840
|
+
const key = new Uint8Array(Buffer.from(requireLegacySecret(bound.credential), "base64"));
|
|
34496
34841
|
await bound.rest.sendMessage(bound.sessionId, localId, encodeMessageContent(content, key));
|
|
34497
34842
|
return { localId };
|
|
34498
34843
|
},
|
|
@@ -34718,18 +35063,21 @@ function normalizeSessionRows(rows, candidateKeys) {
|
|
|
34718
35063
|
for (const row2 of rows) {
|
|
34719
35064
|
const metadata = decryptJsonWithCandidates(row2.metadata, candidateKeys);
|
|
34720
35065
|
if (!metadata) continue;
|
|
34721
|
-
sessions.push(
|
|
34722
|
-
id: row2.id,
|
|
34723
|
-
machineId: readString$4(metadata, "machineId") ?? "",
|
|
34724
|
-
directory: readString$4(metadata, "path") ?? "",
|
|
34725
|
-
summary: readSummary(metadata),
|
|
34726
|
-
active: row2.active === true,
|
|
34727
|
-
updatedAt: row2.updatedAt ?? 0,
|
|
34728
|
-
agentState: decryptJsonWithCandidates(row2.agentState, candidateKeys)
|
|
34729
|
-
});
|
|
35066
|
+
sessions.push(normalizeDecryptedSessionRow(row2, metadata, decryptJsonWithCandidates(row2.agentState, candidateKeys)));
|
|
34730
35067
|
}
|
|
34731
35068
|
return sessions.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
34732
35069
|
}
|
|
35070
|
+
function normalizeDecryptedSessionRow(row2, metadata, agentState) {
|
|
35071
|
+
return {
|
|
35072
|
+
id: row2.id,
|
|
35073
|
+
machineId: readString$4(metadata, "machineId") ?? "",
|
|
35074
|
+
directory: readString$4(metadata, "path") ?? "",
|
|
35075
|
+
summary: readSummary(metadata),
|
|
35076
|
+
active: row2.active === true,
|
|
35077
|
+
updatedAt: row2.updatedAt ?? 0,
|
|
35078
|
+
agentState
|
|
35079
|
+
};
|
|
35080
|
+
}
|
|
34733
35081
|
function filterSessions(sessions, options) {
|
|
34734
35082
|
return sessions.filter((session) => {
|
|
34735
35083
|
if (!options.all && !session.active) return false;
|
|
@@ -34746,7 +35094,7 @@ async function resumeStoppedSession(context, transport, sessionId) {
|
|
|
34746
35094
|
const rows = await context.rest.lookupSessions([sessionId]).catch((error) => {
|
|
34747
35095
|
throw new CliError(`Saycode could not check whether this conversation is running: ${error instanceof Error ? error.message : String(error)}`);
|
|
34748
35096
|
});
|
|
34749
|
-
const machineId = stoppedSessionMachine(rows, sessionId, [decodeBase64$1(context.syncCredential
|
|
35097
|
+
const machineId = stoppedSessionMachine(rows, sessionId, [decodeBase64$1(requireLegacySecret(context.syncCredential))]);
|
|
34750
35098
|
if (!machineId) return;
|
|
34751
35099
|
const result2 = await transport.rpc(machineId, "resume-happy-session", { sessionId }, 6e4).catch((error) => {
|
|
34752
35100
|
throw new CliError(`This conversation is stopped and could not be started again on its machine: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -35097,8 +35445,8 @@ function collectRelatedProjectIds(links, activeProjectId) {
|
|
|
35097
35445
|
}
|
|
35098
35446
|
function mergeRelatedProjectDirectorySkipCounts(...counts) {
|
|
35099
35447
|
const merged = {};
|
|
35100
|
-
for (const
|
|
35101
|
-
for (const [reason, count] of Object.entries(
|
|
35448
|
+
for (const entries2 of counts) {
|
|
35449
|
+
for (const [reason, count] of Object.entries(entries2)) {
|
|
35102
35450
|
if (count) incrementSkip(merged, reason, count);
|
|
35103
35451
|
}
|
|
35104
35452
|
}
|
|
@@ -35545,6 +35893,44 @@ async function resolveSpawnBrowserAttestation(input) {
|
|
|
35545
35893
|
return void 0;
|
|
35546
35894
|
}
|
|
35547
35895
|
}
|
|
35896
|
+
async function sessionControlRpc(sessionId, method, params, resolveTransport, beforeSend) {
|
|
35897
|
+
const encryption = resolveSessionWriteEncryption(sessionId);
|
|
35898
|
+
if (encryption.kind === "blocked") throw new SessionWriteLockedError();
|
|
35899
|
+
const guard = () => {
|
|
35900
|
+
const current2 = resolveSessionWriteEncryption(sessionId);
|
|
35901
|
+
if (current2.kind !== encryption.kind || current2.kind === "dek" && encryption.kind === "dek" && current2.dek !== encryption.dek) {
|
|
35902
|
+
throw new SessionWriteLockedError();
|
|
35903
|
+
}
|
|
35904
|
+
};
|
|
35905
|
+
const transport = await resolveTransport();
|
|
35906
|
+
try {
|
|
35907
|
+
guard();
|
|
35908
|
+
const nonce = Buffer$1.from(crypto.getRandomValues(new Uint8Array(16))).toString("base64");
|
|
35909
|
+
const encrypted = encryption.kind === "dek" ? await encryptDataKeyContent(encryption.dek, { rpcBinding: 1, method, scope: sessionId, issuedAt: Date.now(), nonce, params }) : transport.secretKey ? encryptSecretBox(JSON.stringify(params), transport.secretKey) : plainJsonToBase64(params);
|
|
35910
|
+
await beforeSend?.();
|
|
35911
|
+
guard();
|
|
35912
|
+
const response2 = await transport.emitWithAck("rpc-call", { method: `${sessionId}:${method}`, params: encrypted });
|
|
35913
|
+
guard();
|
|
35914
|
+
if (!response2.ok) throw new Error(response2.error || "SESSION_RPC_FAILED");
|
|
35915
|
+
let result2 = null;
|
|
35916
|
+
if (encryption.kind === "dek") {
|
|
35917
|
+
const opened = response2.result ? await decryptDataKeyJson(encryption.dek, response2.result) : null;
|
|
35918
|
+
guard();
|
|
35919
|
+
if (!opened || opened.rpcBinding !== 1 || opened.nonce !== nonce || !Object.hasOwn(opened, "result") || Object.hasOwn(opened, "params") || opened.result === null && method !== "permission") throw new Error("SESSION_RPC_BINDING_FAILED");
|
|
35920
|
+
result2 = opened.result;
|
|
35921
|
+
} else if (response2.result) {
|
|
35922
|
+
result2 = JSON.parse(/^[{[\"]/.test(response2.result) ? response2.result : transport.secretKey ? decryptSecretBox(response2.result, transport.secretKey) : Buffer$1.from(response2.result, "base64").toString("utf8"));
|
|
35923
|
+
}
|
|
35924
|
+
if (result2 && typeof result2 === "object") {
|
|
35925
|
+
const value2 = result2;
|
|
35926
|
+
if (value2.error || value2.success === false || value2.ok === false) throw new Error(typeof value2.error === "string" ? value2.error : "SESSION_RPC_FAILED");
|
|
35927
|
+
return value2;
|
|
35928
|
+
}
|
|
35929
|
+
return null;
|
|
35930
|
+
} finally {
|
|
35931
|
+
transport.dispose();
|
|
35932
|
+
}
|
|
35933
|
+
}
|
|
35548
35934
|
async function mapWithConcurrency(values, concurrency, mapper) {
|
|
35549
35935
|
const results = new Array(values.length);
|
|
35550
35936
|
let nextIndex = 0;
|
|
@@ -36449,58 +36835,32 @@ function isRpcMethodNotAvailableError(error) {
|
|
|
36449
36835
|
function sessionPermissionUpgradeMessage() {
|
|
36450
36836
|
return translateCurrent("sessionCreation.daemonUpgrade");
|
|
36451
36837
|
}
|
|
36452
|
-
function parsePermissionReplyAck(response2, key) {
|
|
36453
|
-
if (!response2.result) return { delivered: null };
|
|
36454
|
-
try {
|
|
36455
|
-
const ack = parseRpcResult(response2.result, key);
|
|
36456
|
-
if (typeof ack?.delivered !== "boolean") return { delivered: null };
|
|
36457
|
-
return { delivered: ack.delivered };
|
|
36458
|
-
} catch {
|
|
36459
|
-
return { delivered: null };
|
|
36460
|
-
}
|
|
36461
|
-
}
|
|
36462
36838
|
async function sendSessionPermission(input, approved) {
|
|
36463
|
-
const
|
|
36464
|
-
|
|
36465
|
-
|
|
36466
|
-
|
|
36467
|
-
|
|
36468
|
-
|
|
36469
|
-
|
|
36470
|
-
error.code = "PERMISSION_FENCED";
|
|
36471
|
-
throw error;
|
|
36472
|
-
}
|
|
36473
|
-
}
|
|
36474
|
-
const params = {
|
|
36475
|
-
id: input.permissionId,
|
|
36476
|
-
approved,
|
|
36477
|
-
decision: approved ? "approved" : "denied",
|
|
36478
|
-
...approved && input.updatedInput ? { updatedInput: input.updatedInput } : {},
|
|
36479
|
-
...approved && input.mode ? { mode: input.mode } : {},
|
|
36480
|
-
...input.channelClaim ? { channelClaim: input.channelClaim } : {}
|
|
36481
|
-
};
|
|
36482
|
-
if (input.beforeSend) {
|
|
36483
|
-
try {
|
|
36484
|
-
await input.beforeSend();
|
|
36485
|
-
} catch {
|
|
36486
|
-
const error = new Error("the permission answer was withdrawn before it was sent");
|
|
36487
|
-
error.code = "PERMISSION_FENCED";
|
|
36488
|
-
throw error;
|
|
36489
|
-
}
|
|
36490
|
-
}
|
|
36491
|
-
const paramsText = JSON.stringify(params);
|
|
36492
|
-
const encryptedParams = transport.secretKey ? encryptSecretBox(paramsText, transport.secretKey) : plainJsonToBase64(params);
|
|
36493
|
-
const response2 = await transport.emitWithAck("rpc-call", {
|
|
36494
|
-
method: `${input.sessionId}:permission`,
|
|
36495
|
-
params: encryptedParams
|
|
36496
|
-
});
|
|
36497
|
-
if (!response2.ok) {
|
|
36498
|
-
throw new Error(response2.error || translateCurrent("sessionCreation.permissionRejected"));
|
|
36839
|
+
const beforeSend = async () => {
|
|
36840
|
+
try {
|
|
36841
|
+
await input.beforeSend?.();
|
|
36842
|
+
} catch {
|
|
36843
|
+
const error = new Error("the permission answer was withdrawn before it was sent");
|
|
36844
|
+
error.code = "PERMISSION_FENCED";
|
|
36845
|
+
throw error;
|
|
36499
36846
|
}
|
|
36500
|
-
|
|
36501
|
-
|
|
36502
|
-
|
|
36503
|
-
|
|
36847
|
+
};
|
|
36848
|
+
const params = {
|
|
36849
|
+
id: input.permissionId,
|
|
36850
|
+
approved,
|
|
36851
|
+
decision: approved ? "approved" : "denied",
|
|
36852
|
+
...approved && input.updatedInput ? { updatedInput: input.updatedInput } : {},
|
|
36853
|
+
...approved && input.mode ? { mode: input.mode } : {},
|
|
36854
|
+
...input.channelClaim ? { channelClaim: input.channelClaim } : {}
|
|
36855
|
+
};
|
|
36856
|
+
const ack = await sessionControlRpc(
|
|
36857
|
+
input.sessionId,
|
|
36858
|
+
"permission",
|
|
36859
|
+
params,
|
|
36860
|
+
() => resolveSessionRpcTransport(input.auth, input.machine),
|
|
36861
|
+
beforeSend
|
|
36862
|
+
);
|
|
36863
|
+
return { delivered: typeof ack?.delivered === "boolean" ? ack.delivered : null };
|
|
36504
36864
|
}
|
|
36505
36865
|
async function replyToSessionPermission(input, approved) {
|
|
36506
36866
|
try {
|
|
@@ -36625,41 +36985,22 @@ async function resumeDesktopSessionOnMachine(input, deps = {}) {
|
|
|
36625
36985
|
}
|
|
36626
36986
|
async function abortDesktopSessionResponse(input) {
|
|
36627
36987
|
if (!input.machine.online) throw new Error(translateCurrent("sessionCreation.machineOfflineInterrupt"));
|
|
36628
|
-
const transport = await resolveSessionRpcTransport(input.auth, input.machine);
|
|
36629
36988
|
try {
|
|
36630
|
-
|
|
36631
|
-
const encryptedParams = transport.secretKey ? encryptSecretBox(paramsText, transport.secretKey) : plainJsonToBase64({});
|
|
36632
|
-
const response2 = await transport.emitWithAck("rpc-call", {
|
|
36633
|
-
method: `${input.sessionId}:abort`,
|
|
36634
|
-
params: encryptedParams
|
|
36635
|
-
});
|
|
36636
|
-
if (!response2.ok) {
|
|
36637
|
-
throw new Error(response2.error || translateCurrent("sessionCreation.interruptFailed"));
|
|
36638
|
-
}
|
|
36989
|
+
await sessionControlRpc(input.sessionId, "abort", {}, () => resolveSessionRpcTransport(input.auth, input.machine));
|
|
36639
36990
|
} catch (error) {
|
|
36640
36991
|
const message = error instanceof Error ? error.message : String(error);
|
|
36641
36992
|
throw new Error(message || translateCurrent("sessionCreation.interruptFailed"));
|
|
36642
|
-
} finally {
|
|
36643
|
-
transport.dispose();
|
|
36644
36993
|
}
|
|
36645
36994
|
}
|
|
36646
36995
|
async function steerDesktopSessionResponse(input) {
|
|
36647
36996
|
if (!input.machine.online) {
|
|
36648
36997
|
return { success: false, error: translateCurrent("sessionCreation.machineOfflineInterrupt") };
|
|
36649
36998
|
}
|
|
36650
|
-
const transport = await resolveSessionRpcTransport(input.auth, input.machine);
|
|
36651
36999
|
try {
|
|
36652
|
-
|
|
36653
|
-
|
|
36654
|
-
const response2 = await transport.emitWithAck("rpc-call", {
|
|
36655
|
-
method: `${input.sessionId}:steer`,
|
|
36656
|
-
params: encryptedParams
|
|
36657
|
-
});
|
|
36658
|
-
return response2.ok ? { success: true } : { success: false, error: response2.error || translateCurrent("sessionCreation.interruptFailed") };
|
|
37000
|
+
await sessionControlRpc(input.sessionId, "steer", { text: input.text }, () => resolveSessionRpcTransport(input.auth, input.machine));
|
|
37001
|
+
return { success: true };
|
|
36659
37002
|
} catch (error) {
|
|
36660
37003
|
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
36661
|
-
} finally {
|
|
36662
|
-
transport.dispose();
|
|
36663
37004
|
}
|
|
36664
37005
|
}
|
|
36665
37006
|
const HUB_MAX_SPAWN = 32;
|
|
@@ -38257,7 +38598,7 @@ function matchMachine(machines2, query) {
|
|
|
38257
38598
|
return { type: "not-found" };
|
|
38258
38599
|
}
|
|
38259
38600
|
async function runMachines(context) {
|
|
38260
|
-
const keys2 = [decodeBase64$1(context.syncCredential
|
|
38601
|
+
const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
|
|
38261
38602
|
const machines2 = normalizeMachineRows(await context.rest.listMachines(), keys2);
|
|
38262
38603
|
if (machines2.length === 0) {
|
|
38263
38604
|
context.out.raw("No machines registered. Run `happy auth login` on a machine to add one.");
|
|
@@ -38268,7 +38609,7 @@ async function runMachines(context) {
|
|
|
38268
38609
|
for (const machine of sorted) context.out.raw(formatMachineRow(machine, now));
|
|
38269
38610
|
}
|
|
38270
38611
|
async function resolveMachineId(context, query) {
|
|
38271
|
-
const keys2 = [decodeBase64$1(context.syncCredential
|
|
38612
|
+
const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
|
|
38272
38613
|
const machines2 = normalizeMachineRows(await context.rest.listMachines(), keys2);
|
|
38273
38614
|
const match = matchMachine(machines2, query);
|
|
38274
38615
|
if (match.type === "match") return match.machine.id;
|
|
@@ -38280,7 +38621,7 @@ ${candidates2}
|
|
|
38280
38621
|
Use a longer prefix or the machine id.`);
|
|
38281
38622
|
}
|
|
38282
38623
|
async function runLs(context, options) {
|
|
38283
|
-
const keys2 = [decodeBase64$1(context.syncCredential
|
|
38624
|
+
const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
|
|
38284
38625
|
const machineId = options.machine ? await resolveMachineId(context, options.machine) : void 0;
|
|
38285
38626
|
const [machineRows, sessionRows] = await Promise.all([
|
|
38286
38627
|
context.rest.listMachines(),
|
|
@@ -38387,7 +38728,7 @@ async function runProjects(context, options) {
|
|
|
38387
38728
|
context.out.raw(options.machine ? "No projects on that machine." : "No projects in this workspace.");
|
|
38388
38729
|
return;
|
|
38389
38730
|
}
|
|
38390
|
-
const keys2 = [decodeBase64$1(context.syncCredential
|
|
38731
|
+
const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
|
|
38391
38732
|
const hostById = new Map(
|
|
38392
38733
|
normalizeMachineRows(await context.rest.listMachines(), keys2).map((machine) => [machine.id, machine.host])
|
|
38393
38734
|
);
|
|
@@ -38621,7 +38962,7 @@ async function runChat(context, options, providedTransport) {
|
|
|
38621
38962
|
let liveSettings = newConversationOptions ? createLiveChatSettings(newConversationOptions) : liveChatSettingsFromSessionRows(
|
|
38622
38963
|
await context.rest.listSessions().catch(() => []),
|
|
38623
38964
|
sessionId,
|
|
38624
|
-
[decodeBase64$1(context.syncCredential
|
|
38965
|
+
[decodeBase64$1(requireLegacySecret(context.syncCredential))],
|
|
38625
38966
|
options.agent
|
|
38626
38967
|
);
|
|
38627
38968
|
const sessionConnection = () => createCliSessionConnection({
|
|
@@ -38815,7 +39156,7 @@ Leaving. Reattach with \`saycode chat -s ${sessionId}\`.`);
|
|
|
38815
39156
|
liveSettings,
|
|
38816
39157
|
await context.rest.listSessions().catch(() => []),
|
|
38817
39158
|
sessionId,
|
|
38818
|
-
[decodeBase64$1(context.syncCredential
|
|
39159
|
+
[decodeBase64$1(requireLegacySecret(context.syncCredential))]
|
|
38819
39160
|
);
|
|
38820
39161
|
}
|
|
38821
39162
|
const liveCommand = localPrompt === null ? { handled: false, settings: liveSettings } : await handleLiveChatCommand(
|
|
@@ -39080,7 +39421,7 @@ function paint$1(context, rows, cursor, selectable) {
|
|
|
39080
39421
|
context.out.raw(out.join("\n"));
|
|
39081
39422
|
}
|
|
39082
39423
|
async function runWatch(context, options) {
|
|
39083
|
-
const keys2 = [decodeBase64$1(context.syncCredential
|
|
39424
|
+
const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
|
|
39084
39425
|
const selectable = process.stdin.isTTY === true;
|
|
39085
39426
|
await withTransport(context, async (transport) => {
|
|
39086
39427
|
let activity = createActivityIndex();
|
|
@@ -39267,7 +39608,7 @@ function screenHeight() {
|
|
|
39267
39608
|
}
|
|
39268
39609
|
async function loadLevel(context, state) {
|
|
39269
39610
|
const level = currentLevel(state);
|
|
39270
|
-
const keys2 = [decodeBase64$1(context.syncCredential
|
|
39611
|
+
const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
|
|
39271
39612
|
if (level === "workspace") {
|
|
39272
39613
|
const workspaces = await context.listWorkspaces();
|
|
39273
39614
|
return workspaces.map((workspace) => ({ id: workspace.id, label: workspace.name, detail: workspace.kind }));
|
|
@@ -40957,10 +41298,10 @@ function serverLogWriter(layout, name) {
|
|
|
40957
41298
|
};
|
|
40958
41299
|
}
|
|
40959
41300
|
const happyCliPackageName = "@buzzni/happy-cli";
|
|
40960
|
-
const happyCliVersion = "1.1.10-aplus.
|
|
40961
|
-
const happyCliTag = "happy-cli-v1.1.10-aplus.
|
|
40962
|
-
const vendorCommit = "
|
|
40963
|
-
const saycodeCliVersion = "0.
|
|
41301
|
+
const happyCliVersion = "1.1.10-aplus.321";
|
|
41302
|
+
const happyCliTag = "happy-cli-v1.1.10-aplus.321";
|
|
41303
|
+
const vendorCommit = "6cd0106d427421c1661c24b18a1351f1e1046f8c";
|
|
41304
|
+
const saycodeCliVersion = "0.9.3";
|
|
40964
41305
|
const happyServerVersion = "0.1.2";
|
|
40965
41306
|
const releaseFeatures = { "mcpRuntimeRecovery": { "enabled": true, "releaseState": "released", "sourceBranch": "main", "sourceHead": "a79a10e288cf7775cb15955f716693c6a7df1640", "requiredCommit": "975e32cf" } };
|
|
40966
41307
|
const pin = {
|
|
@@ -41289,7 +41630,7 @@ class ProcessLedger {
|
|
|
41289
41630
|
} catch {
|
|
41290
41631
|
return [];
|
|
41291
41632
|
}
|
|
41292
|
-
const
|
|
41633
|
+
const entries2 = [];
|
|
41293
41634
|
for (const name of names) {
|
|
41294
41635
|
if (!name.endsWith(".json")) continue;
|
|
41295
41636
|
let raw;
|
|
@@ -41307,9 +41648,9 @@ class ProcessLedger {
|
|
|
41307
41648
|
this.log(`ignoring ledger entry ${name} whose recorded id is ${entry.id}`);
|
|
41308
41649
|
continue;
|
|
41309
41650
|
}
|
|
41310
|
-
|
|
41651
|
+
entries2.push(entry);
|
|
41311
41652
|
}
|
|
41312
|
-
return
|
|
41653
|
+
return entries2;
|
|
41313
41654
|
}
|
|
41314
41655
|
/**
|
|
41315
41656
|
* A record we cannot read is one no sweep will ever act on, so it is moved
|
|
@@ -41392,11 +41733,11 @@ class ProcessLedger {
|
|
|
41392
41733
|
return this.reconciliation;
|
|
41393
41734
|
}
|
|
41394
41735
|
async runReconciliation() {
|
|
41395
|
-
const
|
|
41736
|
+
const entries2 = await this.readEntries();
|
|
41396
41737
|
const probe = this.options.probe ?? ((entry) => probeProcess(entry));
|
|
41397
41738
|
const terminate = this.options.terminate ?? terminateProcess;
|
|
41398
41739
|
const probes = {};
|
|
41399
|
-
await Promise.all(
|
|
41740
|
+
await Promise.all(entries2.map(async (entry) => {
|
|
41400
41741
|
try {
|
|
41401
41742
|
probes[entry.id] = await probe(entry);
|
|
41402
41743
|
} catch (error) {
|
|
@@ -41404,7 +41745,7 @@ class ProcessLedger {
|
|
|
41404
41745
|
probes[entry.id] = "unknown";
|
|
41405
41746
|
}
|
|
41406
41747
|
}));
|
|
41407
|
-
const plan = planLedgerReconciliation({ entries, probes, selfPid: process.pid });
|
|
41748
|
+
const plan = planLedgerReconciliation({ entries: entries2, probes, selfPid: process.pid });
|
|
41408
41749
|
for (const action of plan.dropOnly) {
|
|
41409
41750
|
this.log(`dropping ${action.entry.kind} pid ${action.entry.pid} (${action.entry.id}): ${action.reason}`);
|
|
41410
41751
|
await this.release(action.entry.id);
|
|
@@ -41481,7 +41822,7 @@ function parseSessionWriteScopeRequest(value2) {
|
|
|
41481
41822
|
...typeof v.error === "string" && /^[A-Z_]{1,80}$/.test(v.error) ? { error: v.error } : {}
|
|
41482
41823
|
};
|
|
41483
41824
|
}
|
|
41484
|
-
function record$
|
|
41825
|
+
function record$7(value2) {
|
|
41485
41826
|
return value2 && typeof value2 === "object" ? value2 : {};
|
|
41486
41827
|
}
|
|
41487
41828
|
class SessionWriteScopeHost {
|
|
@@ -41505,8 +41846,8 @@ class SessionWriteScopeHost {
|
|
|
41505
41846
|
};
|
|
41506
41847
|
}
|
|
41507
41848
|
async list(machineId, sessionId) {
|
|
41508
|
-
const body = record$
|
|
41509
|
-
const capability = record$
|
|
41849
|
+
const body = record$7(await this.post("/session-write-scope", { action: "list", sessionId }));
|
|
41850
|
+
const capability = record$7(record$7(body.capabilities).sessionWriteScope);
|
|
41510
41851
|
if (body.supported !== true || capability.version !== 1 || capability.incarnation !== this.incarnation || capability.hostKey !== this.keyFingerprint || capability.accountId !== this.accountId || capability.machineId !== machineId) {
|
|
41511
41852
|
return { supported: false, requests: [] };
|
|
41512
41853
|
}
|
|
@@ -41540,7 +41881,7 @@ class SessionWriteScopeHost {
|
|
|
41540
41881
|
decision.sessionId,
|
|
41541
41882
|
decision.action
|
|
41542
41883
|
]));
|
|
41543
|
-
const response2 = record$
|
|
41884
|
+
const response2 = record$7(await this.post("/session-write-scope/decide", {
|
|
41544
41885
|
decision,
|
|
41545
41886
|
signature: sign(null, bytes, this.keys.privateKey).toString("base64url")
|
|
41546
41887
|
}));
|
|
@@ -41554,7 +41895,7 @@ class SessionWriteScopeHost {
|
|
|
41554
41895
|
async requestRevocation(machineId, sessionId, requestId) {
|
|
41555
41896
|
const request2 = (await this.list(machineId, sessionId)).requests.find((item) => item.id === requestId);
|
|
41556
41897
|
if (!request2 || request2.kind !== "grant" || request2.grantActive !== true) throw new Error("GRANT_NOT_ACTIVE");
|
|
41557
|
-
const body = record$
|
|
41898
|
+
const body = record$7(await this.post("/session-write-scope", {
|
|
41558
41899
|
action: "request",
|
|
41559
41900
|
sessionId,
|
|
41560
41901
|
path: request2.root,
|
|
@@ -41624,8 +41965,8 @@ function parsePersistedPathOutput(stdout, env) {
|
|
|
41624
41965
|
try {
|
|
41625
41966
|
const parsed2 = JSON.parse(Buffer.from(stdout.trim(), "base64").toString("utf8"));
|
|
41626
41967
|
const raw = [parsed2.machine, parsed2.user].filter((value2) => typeof value2 === "string").join(";");
|
|
41627
|
-
const
|
|
41628
|
-
return
|
|
41968
|
+
const entries2 = mergeWindowsPathEntries([], raw.split(";").map((entry) => expandWindowsEnvReferences(entry.trim(), env)));
|
|
41969
|
+
return entries2.length > 0 ? entries2 : null;
|
|
41629
41970
|
} catch {
|
|
41630
41971
|
return null;
|
|
41631
41972
|
}
|
|
@@ -41648,14 +41989,14 @@ function mergeWindowsPathEntries(current2, extra) {
|
|
|
41648
41989
|
}
|
|
41649
41990
|
function createWindowsPersistedPathReader(input) {
|
|
41650
41991
|
const now = input.now ?? Date.now;
|
|
41651
|
-
let
|
|
41992
|
+
let entries2 = null;
|
|
41652
41993
|
let readAt = -Infinity;
|
|
41653
41994
|
let failedAt = -Infinity;
|
|
41654
41995
|
let pending2 = null;
|
|
41655
41996
|
return {
|
|
41656
41997
|
/** Single flight; a failed read keeps the last good value so a flaky PowerShell never drops known entries. */
|
|
41657
41998
|
refresh() {
|
|
41658
|
-
if (now() - readAt < FRESH_MS || now() - failedAt < FAILURE_COOLDOWN_MS) return Promise.resolve(
|
|
41999
|
+
if (now() - readAt < FRESH_MS || now() - failedAt < FAILURE_COOLDOWN_MS) return Promise.resolve(entries2);
|
|
41659
42000
|
pending2 ??= (async () => {
|
|
41660
42001
|
const env = input.env();
|
|
41661
42002
|
const powershell = win32.join(env.SystemRoot ?? "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
@@ -41663,20 +42004,20 @@ function createWindowsPersistedPathReader(input) {
|
|
|
41663
42004
|
const stdout = await input.run(powershell, ["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(SCRIPT, "utf16le").toString("base64")], { timeout: TIMEOUT_MS, env });
|
|
41664
42005
|
const next = parsePersistedPathOutput(stdout, env);
|
|
41665
42006
|
if (next) {
|
|
41666
|
-
|
|
42007
|
+
entries2 = next;
|
|
41667
42008
|
readAt = now();
|
|
41668
42009
|
} else failedAt = now();
|
|
41669
42010
|
} catch {
|
|
41670
42011
|
failedAt = now();
|
|
41671
42012
|
}
|
|
41672
|
-
return
|
|
42013
|
+
return entries2;
|
|
41673
42014
|
})().finally(() => {
|
|
41674
42015
|
pending2 = null;
|
|
41675
42016
|
});
|
|
41676
42017
|
return pending2;
|
|
41677
42018
|
},
|
|
41678
42019
|
cached() {
|
|
41679
|
-
return
|
|
42020
|
+
return entries2;
|
|
41680
42021
|
}
|
|
41681
42022
|
};
|
|
41682
42023
|
}
|
|
@@ -41722,9 +42063,9 @@ function commandEnvironment(env, paths = [], platform2 = process.platform, persi
|
|
|
41722
42063
|
const keys2 = Object.keys(next).filter((key2) => windows ? key2.toLowerCase() === "path" : key2 === "PATH");
|
|
41723
42064
|
const key = windows && keys2.includes("Path") ? "Path" : "PATH";
|
|
41724
42065
|
const delimiter2 = windows ? ";" : ":";
|
|
41725
|
-
const
|
|
42066
|
+
const entries2 = keys2.flatMap((name) => (next[name] ?? "").split(delimiter2)).filter(Boolean);
|
|
41726
42067
|
for (const name of keys2) delete next[name];
|
|
41727
|
-
next[key] = [.../* @__PURE__ */ new Set([...windows ? mergeWindowsPathEntries(
|
|
42068
|
+
next[key] = [.../* @__PURE__ */ new Set([...windows ? mergeWindowsPathEntries(entries2, persisted ?? []) : entries2, ...discoveredPaths, ...paths])].join(delimiter2);
|
|
41728
42069
|
return next;
|
|
41729
42070
|
}
|
|
41730
42071
|
function rememberCommandPaths(paths) {
|
|
@@ -41792,8 +42133,8 @@ function parseUserShellPath(stdout) {
|
|
|
41792
42133
|
const start = stdout.indexOf(START);
|
|
41793
42134
|
const end = stdout.indexOf(END, start + START.length);
|
|
41794
42135
|
if (start < 0 || end < 0) return null;
|
|
41795
|
-
const
|
|
41796
|
-
return
|
|
42136
|
+
const entries2 = stdout.slice(start + START.length, end).split(":").filter((entry) => entry.startsWith("/"));
|
|
42137
|
+
return entries2.length > 0 ? entries2.join(":") : null;
|
|
41797
42138
|
}
|
|
41798
42139
|
function resolveUserShell(env, platform2) {
|
|
41799
42140
|
if (env.SHELL?.startsWith("/")) return env.SHELL;
|
|
@@ -41898,8 +42239,8 @@ const GLOBAL_HAPPY_CLI_PACKAGE = "@buzzni/happy-cli";
|
|
|
41898
42239
|
function happyCliEnv(userShellPath = cachedUserShellPath()) {
|
|
41899
42240
|
if (process.platform === "win32") return commandEnvironment(process.env);
|
|
41900
42241
|
const extraPaths = ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"];
|
|
41901
|
-
const
|
|
41902
|
-
return commandEnvironment({ ...process.env, PATH: [...new Set(
|
|
42242
|
+
const entries2 = [userShellPath, process.env.PATH, ...extraPaths].filter(Boolean).join(":").split(":");
|
|
42243
|
+
return commandEnvironment({ ...process.env, PATH: [...new Set(entries2)].join(":") });
|
|
41903
42244
|
}
|
|
41904
42245
|
async function happyCliEnvWithUserShell() {
|
|
41905
42246
|
await ensureUserShellPath();
|
|
@@ -42112,7 +42453,7 @@ function validateCandidateInventory(inventory) {
|
|
|
42112
42453
|
return inventory;
|
|
42113
42454
|
}
|
|
42114
42455
|
async function verifyCandidateFiles(root, inventory) {
|
|
42115
|
-
const
|
|
42456
|
+
const entries2 = validateCandidateInventory(inventory);
|
|
42116
42457
|
const directories = /* @__PURE__ */ new Map();
|
|
42117
42458
|
const checkDirectory = (directory) => {
|
|
42118
42459
|
if (!directories.has(directory)) directories.set(directory, fs.promises.lstat(directory).then((stat2) => {
|
|
@@ -42120,8 +42461,8 @@ async function verifyCandidateFiles(root, inventory) {
|
|
|
42120
42461
|
}));
|
|
42121
42462
|
return directories.get(directory);
|
|
42122
42463
|
};
|
|
42123
|
-
for (let offset = 0; offset <
|
|
42124
|
-
await Promise.all(
|
|
42464
|
+
for (let offset = 0; offset < entries2.length; offset += 16) {
|
|
42465
|
+
await Promise.all(entries2.slice(offset, offset + 16).map(async (entry) => {
|
|
42125
42466
|
let directory = root;
|
|
42126
42467
|
await checkDirectory(directory);
|
|
42127
42468
|
const parts2 = entry.file.split("/");
|
|
@@ -42380,7 +42721,7 @@ function waitExit(child, timeout) {
|
|
|
42380
42721
|
});
|
|
42381
42722
|
}
|
|
42382
42723
|
const identifier$2 = (value2) => typeof value2 === "string" && /^[a-zA-Z0-9._-]{1,128}$/.test(value2);
|
|
42383
|
-
const record$
|
|
42724
|
+
const record$6 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
42384
42725
|
const pause = (ms2) => new Promise((resolve2) => setTimeout(resolve2, ms2));
|
|
42385
42726
|
class StandaloneDaemonRecoveryRequiredError extends Error {
|
|
42386
42727
|
#recover;
|
|
@@ -42419,7 +42760,7 @@ async function drainAndStopStandaloneDaemon(child, state, {
|
|
|
42419
42760
|
reason = "app-quit",
|
|
42420
42761
|
isDaemonIncarnationGone = async () => false
|
|
42421
42762
|
} = {}) {
|
|
42422
|
-
if (!record$
|
|
42763
|
+
if (!record$6(state) || !Number.isInteger(state.pid) || state.pid < 1 || child && state.pid !== child.pid || !Number.isInteger(state.httpPort) || state.httpPort < 1 || state.httpPort > 65535 || typeof state.controlSecret !== "string" || !state.controlSecret || !["app-quit", "update", "mode-switch"].includes(reason)) {
|
|
42423
42764
|
throw new Error("Invalid standalone daemon control state");
|
|
42424
42765
|
}
|
|
42425
42766
|
const { httpPort, controlSecret } = state;
|
|
@@ -42462,13 +42803,13 @@ async function drainAndStopStandaloneDaemon(child, state, {
|
|
|
42462
42803
|
}
|
|
42463
42804
|
};
|
|
42464
42805
|
const capability = await post("capabilities", {});
|
|
42465
|
-
if (!record$
|
|
42806
|
+
if (!record$6(capability) || capability.supported !== true || capability.capability !== "standalone-session-drain-v1" || !identifier$2(capability.instanceId) || !Array.isArray(capability.targets) || capability.targets.length < 1 || capability.targets.length > 8 || capability.targets.some((target) => !record$6(target) || target.platform !== "win32" || !["x64", "arm64"].includes(target.arch) || !["codex", "claude", "opencode", "grok"].includes(target.provider) || target.mode !== "standard")) throw new Error("Standalone drain unsupported or invalid capability");
|
|
42466
42807
|
const expectedInstanceId = capability.instanceId;
|
|
42467
42808
|
const requestId = randomUUID();
|
|
42468
42809
|
let operationId;
|
|
42469
42810
|
const observedLaunches = /* @__PURE__ */ new Set();
|
|
42470
42811
|
const snapshot = (value2) => {
|
|
42471
|
-
if (!record$
|
|
42812
|
+
if (!record$6(value2) || value2.version !== 1 || value2.instanceId !== expectedInstanceId || !identifier$2(value2.operationId) || operationId && value2.operationId !== operationId || !["draining", "blocked", "completed"].includes(value2.state) || !(value2.error === null || typeof value2.error === "string" && value2.error.length <= 1024) || typeof value2.retryable !== "boolean" || !Array.isArray(value2.launches) || value2.launches.length > 256 || value2.launches.some((launch) => !record$6(launch) || !identifier$2(launch.launchId) || !["pending", "exited", "unknown", "failed"].includes(launch.state) || launch.canTerminate !== void 0 && typeof launch.canTerminate !== "boolean" || launch.state === "exited" && launch.canTerminate === true) || new Set(value2.launches.map((launch) => launch.launchId)).size !== value2.launches.length) throw new Error("Invalid standalone drain snapshot");
|
|
42472
42813
|
const launchIds = new Set(value2.launches.map((launch) => launch.launchId));
|
|
42473
42814
|
if ([...observedLaunches].some((launchId) => !launchIds.has(launchId))) throw new Error("Standalone drain lost a launch");
|
|
42474
42815
|
for (const launchId of launchIds) observedLaunches.add(launchId);
|
|
@@ -42477,7 +42818,7 @@ async function drainAndStopStandaloneDaemon(child, state, {
|
|
|
42477
42818
|
const blockedOperationId = value2.operationId;
|
|
42478
42819
|
throw new StandaloneDrainBlockedError(value2.launches, async (launchId) => {
|
|
42479
42820
|
const result2 = await post("terminate", { expectedInstanceId, operationId: blockedOperationId, launchId }, performance.now() + 3e4);
|
|
42480
|
-
if (!record$
|
|
42821
|
+
if (!record$6(result2) || result2.accepted !== true) throw new Error("Standalone owner termination unconfirmed");
|
|
42481
42822
|
});
|
|
42482
42823
|
}
|
|
42483
42824
|
if (value2.state === "completed" && (value2.retryable !== false || value2.error !== null || value2.launches.some((launch) => launch.state !== "exited"))) {
|
|
@@ -42491,7 +42832,7 @@ async function drainAndStopStandaloneDaemon(child, state, {
|
|
|
42491
42832
|
status = snapshot(await post("status", { expectedInstanceId, operationId }));
|
|
42492
42833
|
}
|
|
42493
42834
|
const committed = await post("commit", { expectedInstanceId, operationId });
|
|
42494
|
-
if (!record$
|
|
42835
|
+
if (!record$6(committed) || committed.accepted !== true) throw new Error("Standalone drain commit unconfirmed");
|
|
42495
42836
|
if (child) {
|
|
42496
42837
|
if (!await waitExit(child, 5e3)) throw new Error("Standalone daemon exit timed out");
|
|
42497
42838
|
if (child.exitCode !== 0 || child.signalCode !== null) throw new Error("Standalone daemon did not exit cleanly");
|
|
@@ -42514,7 +42855,7 @@ async function stopWindowsStandaloneDaemon({ child, homeDir, external: external2
|
|
|
42514
42855
|
let state;
|
|
42515
42856
|
try {
|
|
42516
42857
|
state = JSON.parse(await readFile(join(homeDir, "daemon.state.json"), "utf8"));
|
|
42517
|
-
if (!record$
|
|
42858
|
+
if (!record$6(state)) throw new Error("Invalid standalone daemon control state");
|
|
42518
42859
|
} catch (error) {
|
|
42519
42860
|
if (error?.code !== "ENOENT") throw new Error("Unreadable standalone daemon control state");
|
|
42520
42861
|
state = null;
|
|
@@ -54410,11 +54751,11 @@ class StandalonePlanningSpec {
|
|
|
54410
54751
|
if (!project.workspaceDir) return { files: [], selected: empty2 };
|
|
54411
54752
|
const requested = rawFilename ? specFilename(rawFilename) : null;
|
|
54412
54753
|
const specsDir = join(project.workspaceDir, "specs");
|
|
54413
|
-
const
|
|
54754
|
+
const entries2 = await readdir(specsDir, { withFileTypes: true }).catch((error) => {
|
|
54414
54755
|
if (error.code === "ENOENT") return [];
|
|
54415
54756
|
throw error;
|
|
54416
54757
|
});
|
|
54417
|
-
const candidates2 =
|
|
54758
|
+
const candidates2 = entries2.filter((entry) => entry.isDirectory()).flatMap((entry) => ["prd.md", "spec.md"].map((file) => `specs/${entry.name}/${file}`)).filter((filename) => {
|
|
54418
54759
|
try {
|
|
54419
54760
|
specFilename(filename);
|
|
54420
54761
|
return true;
|
|
@@ -55159,6 +55500,33 @@ const managedClaudeEn = {
|
|
|
55159
55500
|
"managedClaude.offline": "Connecting to your PC.",
|
|
55160
55501
|
"managedClaude.loading": "Connecting AI…"
|
|
55161
55502
|
};
|
|
55503
|
+
const workTemplateEn = {
|
|
55504
|
+
"work.gallery": "Document templates",
|
|
55505
|
+
"work.templates": "Templates",
|
|
55506
|
+
"work.create": "Create my template",
|
|
55507
|
+
"work.closeGallery": "Close templates",
|
|
55508
|
+
"work.edit": "Edit template",
|
|
55509
|
+
"work.delete": "Delete template",
|
|
55510
|
+
"work.deleteConfirm": "Delete the {name} template?",
|
|
55511
|
+
"work.description": "Save an existing document’s formatting to reuse in your next Work.",
|
|
55512
|
+
"work.name": "Template name",
|
|
55513
|
+
"work.file": "Source file",
|
|
55514
|
+
"work.chooseFile": "Choose file",
|
|
55515
|
+
"work.noFileSelected": "No file selected",
|
|
55516
|
+
"work.preserve": "Formatting and instructions to preserve",
|
|
55517
|
+
"work.save": "Save template",
|
|
55518
|
+
"work.saving": "Saving…",
|
|
55519
|
+
"work.instructions": "Instructions",
|
|
55520
|
+
"work.saveChanges": "Save changes",
|
|
55521
|
+
"work.templateInvalid": "The selected template could not be verified.",
|
|
55522
|
+
"work.templateChanged": "The template changed. Select it again.",
|
|
55523
|
+
"work.templatesUnavailable": "Could not load templates.",
|
|
55524
|
+
"work.clearTemplate": "Clear template",
|
|
55525
|
+
"work.templatesLoading": "Loading templates…",
|
|
55526
|
+
"work.templateSaveFailed": "Could not save the template. Check the contents and try again.",
|
|
55527
|
+
"work.templatePrompt": "Create a {format} document using the {name} template skill. Preserve the original design and structure.",
|
|
55528
|
+
"work.taskCue": "My task is"
|
|
55529
|
+
};
|
|
55162
55530
|
const workEn = {
|
|
55163
55531
|
"work.mediaChanged": "The original file changed. Reopen it to continue.",
|
|
55164
55532
|
"work.mediaUnsupported": "Media streaming is unavailable on this computer. Save the original and open it locally.",
|
|
@@ -55362,6 +55730,7 @@ const organizationStartEn = {
|
|
|
55362
55730
|
"organizationStart.welcome": "Start working with your organization",
|
|
55363
55731
|
"organizationStart.welcomeBody": "We’ll prepare the tools and connect this computer automatically.",
|
|
55364
55732
|
"organizationStart.chooseTitle": "Which organization will you work in?",
|
|
55733
|
+
"organizationStart.resumeVerified": "Continue with verified organization",
|
|
55365
55734
|
"organizationStart.next": "Next",
|
|
55366
55735
|
"organizationStart.switching": "Connecting organization…",
|
|
55367
55736
|
"organizationStart.confirmConnectionTitle": "Confirm PC connection",
|
|
@@ -55514,11 +55883,30 @@ const organizationStartEn = {
|
|
|
55514
55883
|
"companyInvite.declineConfirm": "Confirm decline",
|
|
55515
55884
|
"companyInvite.declineBody": "Decline the invitation from {name}? Joining later requires a new invitation.",
|
|
55516
55885
|
"companyInvite.cancel": "Cancel",
|
|
55517
|
-
"companyInvite.loading": "Checking invitations…",
|
|
55886
|
+
"companyInvite.loading": "Checking organizations and invitations…",
|
|
55518
55887
|
"companyInvite.acceptLabel": "Accept invitation from {name}",
|
|
55519
55888
|
"companyInvite.declineLabel": "Decline invitation from {name}",
|
|
55520
55889
|
"companyInvite.declineConfirmLabel": "Confirm decline of invitation from {name}",
|
|
55521
|
-
"companyInvite.refreshFailed": "Could not
|
|
55890
|
+
"companyInvite.refreshFailed": "Could not check organizations and invitations. Try again.",
|
|
55891
|
+
"organizationEnrollment.join": "Join with an invite code",
|
|
55892
|
+
"organizationEnrollment.create": "Create an organization",
|
|
55893
|
+
"organizationEnrollment.code": "Invite code",
|
|
55894
|
+
"organizationEnrollment.name": "Name",
|
|
55895
|
+
"organizationEnrollment.joinSubmit": "Join organization",
|
|
55896
|
+
"organizationEnrollment.createSubmit": "Create",
|
|
55897
|
+
"organizationEnrollment.back": "Back to organization options",
|
|
55898
|
+
"organizationEnrollment.joinHelp": "Enter the invite code from your organization administrator.",
|
|
55899
|
+
"organizationEnrollment.working": "Checking organization…",
|
|
55900
|
+
"organizationEnrollment.joined": "Organization confirmed. Choose Next to continue setup.",
|
|
55901
|
+
"organizationEnrollment.unconfirmed": "The result is not confirmed yet. Recheck your organizations without repeating the request, then select the organization you joined.",
|
|
55902
|
+
"organizationEnrollment.recheck": "Recheck organizations",
|
|
55903
|
+
"organizationEnrollment.invalidCode": "Check the invite code. Ask your administrator for a new code if it expired or was revoked.",
|
|
55904
|
+
"organizationEnrollment.slugTaken": "Could not prepare the organization address. Try again.",
|
|
55905
|
+
"organizationEnrollment.rateLimited": "Too many requests. Please try again shortly.",
|
|
55906
|
+
"organizationEnrollment.signInAgain": "Your session expired. Sign in again to join.",
|
|
55907
|
+
"organizationEnrollment.failed": "We could not process the request. Check your input and try again.",
|
|
55908
|
+
"organizationEnrollment.welcomeBody": "Choose an organization to work with, or create a new one.",
|
|
55909
|
+
"organizationEnrollment.other": "Choose or join another organization"
|
|
55522
55910
|
};
|
|
55523
55911
|
const usageEntryEn = {
|
|
55524
55912
|
...selfHostConnectEn,
|
|
@@ -55662,7 +56050,38 @@ const usageEntryEn = {
|
|
|
55662
56050
|
"onboarding.additionalStep": "Additional step",
|
|
55663
56051
|
"onboarding.requiredSettings": "Required setup",
|
|
55664
56052
|
"onboarding.additionalSettings": "Additional setup — optional",
|
|
55665
|
-
"onboarding.cli.bundledPreparation": "Saycode CLI is bundled with the app. Prepare the required tools, then register this computer and verify its daemon."
|
|
56053
|
+
"onboarding.cli.bundledPreparation": "Saycode CLI is bundled with the app. Prepare the required tools, then register this computer and verify its daemon.",
|
|
56054
|
+
"signup.open": "Create an account",
|
|
56055
|
+
"signup.title": "Create an account",
|
|
56056
|
+
"signup.sendCode": "Send verification code",
|
|
56057
|
+
"signup.sendingCode": "Sending code…",
|
|
56058
|
+
"signup.code": "Enter the verification code sent to your email",
|
|
56059
|
+
"signup.resend": "Resend",
|
|
56060
|
+
"signup.password": "Password",
|
|
56061
|
+
"signup.confirmation": "Confirm password",
|
|
56062
|
+
"signup.minimum": "12+ characters",
|
|
56063
|
+
"signup.mismatch": "Passwords do not match.",
|
|
56064
|
+
"signup.submit": "Sign up and start",
|
|
56065
|
+
"signup.creating": "Creating your account…",
|
|
56066
|
+
"signup.back": "Sign in",
|
|
56067
|
+
"signup.registered": "This email is already registered. Please log in.",
|
|
56068
|
+
"signup.rateLimited": "Too many requests. Try again later.",
|
|
56069
|
+
"signup.requestFailed": "Could not request a code. Check your connection and try again.",
|
|
56070
|
+
"signup.invalidCode": "The code is invalid or expired. Please check it again.",
|
|
56071
|
+
"signup.recoveryUnavailable": "Account recovery is unavailable. Try again later.",
|
|
56072
|
+
"signup.failed": "Could not complete signup. Check your details and try again.",
|
|
56073
|
+
"signup.unconfirmed": "Signup could not be confirmed. Check your account by logging in.",
|
|
56074
|
+
"signup.complete": "Your account is ready. Log in to start.",
|
|
56075
|
+
"signup.continueLogin": "Log in to start",
|
|
56076
|
+
"signup.terms": "By continuing, you agree to the Terms of Service.",
|
|
56077
|
+
"signup.showPassword": "Show passwords",
|
|
56078
|
+
"signup.hidePassword": "Hide passwords",
|
|
56079
|
+
"signup.enterCode": "Enter verification code",
|
|
56080
|
+
"signup.digit": "Verification code digit {number}",
|
|
56081
|
+
"signup.verify": "Verify and continue",
|
|
56082
|
+
"signup.verifying": "Verifying…",
|
|
56083
|
+
"signup.previous": "Back",
|
|
56084
|
+
"signup.verifyFailed": "Could not verify the code. Check your connection and try again."
|
|
55666
56085
|
};
|
|
55667
56086
|
const homeTabsEn = {
|
|
55668
56087
|
"homeTabs.collaboratingTitle": "Collaborating projects",
|
|
@@ -55720,8 +56139,8 @@ const homeTabsEn = {
|
|
|
55720
56139
|
"homeTabs.startingWork": "Starting work",
|
|
55721
56140
|
"homeTabs.startWorkFailed": "Could not start work.",
|
|
55722
56141
|
"homeTabs.syncedBeforeImplementation": "Synced the workspace plan before implementation",
|
|
55723
|
-
"homeTabs.quick": "
|
|
55724
|
-
"homeTabs.git": "Import repository",
|
|
56142
|
+
"homeTabs.quick": "Create a project",
|
|
56143
|
+
"homeTabs.git": "Import code repository",
|
|
55725
56144
|
"homeTabs.zip": "Import ZIP/files",
|
|
55726
56145
|
"homeTabs.folder": "Use a machine folder",
|
|
55727
56146
|
"homeTabs.categories": "Project categories"
|
|
@@ -56024,7 +56443,15 @@ const workAreaEn = {
|
|
|
56024
56443
|
"workArea.panelFailed": "Couldn't display this view",
|
|
56025
56444
|
"workArea.panelFailedHint": "If retrying doesn't help, close this tab and reopen it from the list. Your conversation and work are kept.",
|
|
56026
56445
|
"workArea.panelRetry": "Try again",
|
|
56027
|
-
"workArea.panelClose": "Close tab"
|
|
56446
|
+
"workArea.panelClose": "Close tab",
|
|
56447
|
+
"workArea.nameSearch.hint": "Filtering list · Enter to search all names",
|
|
56448
|
+
"workArea.nameSearch.loading": "Finding project file names…",
|
|
56449
|
+
"workArea.nameSearch.partial": "Partial file results · Enter to search again",
|
|
56450
|
+
"workArea.nameSearch.results": "Project results · Esc to clear",
|
|
56451
|
+
"workArea.nameSearch.empty": "No matching files · Esc to clear",
|
|
56452
|
+
"workArea.revealFile": "Reveal in file list",
|
|
56453
|
+
"workArea.revealFailed": "Could not locate the file. Try Reveal again.",
|
|
56454
|
+
"workArea.fileListFailed": "Could not load the file list."
|
|
56028
56455
|
};
|
|
56029
56456
|
const redesignEn = {
|
|
56030
56457
|
"newSession.aiAuto": "AI Auto",
|
|
@@ -57172,7 +57599,7 @@ const personalChatsEn = {
|
|
|
57172
57599
|
"personalChats.creating": "Starting the chat…",
|
|
57173
57600
|
"personalChats.noOnlineMachine": "No machine is online. Start one to begin a chat.",
|
|
57174
57601
|
"personalChats.localMachinePreparing": "Connecting this computer to Saycode. You can start in a moment.",
|
|
57175
|
-
"personalChats.localMachineAsk": "
|
|
57602
|
+
"personalChats.localMachineAsk": "Connect this computer as a work PC to start chatting.",
|
|
57176
57603
|
"personalChats.localMachineFailed": "Automatic connection for this computer failed. You can connect another way.",
|
|
57177
57604
|
"personalChats.pickMachine": "Choose a machine to run this chat on",
|
|
57178
57605
|
"personalChats.createFailed": "Could not start the chat.",
|
|
@@ -58562,7 +58989,24 @@ const organizationTeamsEn = {
|
|
|
58562
58989
|
"organizationTeams.shareApply": "Apply"
|
|
58563
58990
|
};
|
|
58564
58991
|
const gitAccountsEn = {
|
|
58565
|
-
"
|
|
58992
|
+
"projectStart.url": "Import from URL",
|
|
58993
|
+
"projectStart.clearRepository": "Clear repository selection",
|
|
58994
|
+
"projectStart.providerPicker": "Choose a {provider} repository",
|
|
58995
|
+
"projectStart.manageAccess": "Manage repository access",
|
|
58996
|
+
"projectStart.repositoryPlaceholder": "Choose a repository",
|
|
58997
|
+
"projectStart.localAccount": "This computer’s GitHub account · @{account}",
|
|
58998
|
+
"projectStart.localGit": "This computer’s Git configuration",
|
|
58999
|
+
"projectStart.machineGit": "Work computer’s Git configuration",
|
|
59000
|
+
"projectStart.githubAccount": "Connected GitHub account",
|
|
59001
|
+
"projectStart.gitlabAccount": "Connected GitLab account",
|
|
59002
|
+
"projectStart.organizationPat": "Organization PAT · {name}",
|
|
59003
|
+
"projectStart.personalPat": "Personal PAT",
|
|
59004
|
+
"projectStart.selectedConnectionHint": "Uses the selected repository’s connected account.",
|
|
59005
|
+
"projectStart.localAccountHint": "Imports using the GitHub account signed in on this computer.",
|
|
59006
|
+
"projectStart.machineGitHint": "Uses Git authentication configured on the work computer.",
|
|
59007
|
+
"projectStart.accountHint": "Accesses the repository using the selected connection.",
|
|
59008
|
+
"projectStart.connectionUnavailable": "The selected repository connection is unavailable. Check its connection status.",
|
|
59009
|
+
"settingsTab.gitAccounts": "Repositories",
|
|
58566
59010
|
"gitAccounts.title": "GitHub · GitLab accounts",
|
|
58567
59011
|
"gitAccounts.description": "Connect the accounts used for repository import, triggers, and PR work. Tokens are kept on the server only and never stored on this device.",
|
|
58568
59012
|
"gitAccounts.refresh": "Refresh status",
|
|
@@ -58578,7 +59022,27 @@ const gitAccountsEn = {
|
|
|
58578
59022
|
"gitAccounts.connectFailed": "Could not start connecting. Try again in a moment.",
|
|
58579
59023
|
"gitAccounts.disconnectFailed": "Could not disconnect. Try again in a moment.",
|
|
58580
59024
|
"gitAccounts.warnings": "Disconnected, but some items need attention:",
|
|
58581
|
-
"
|
|
59025
|
+
"projectStart.repositorySettings": "Repository settings",
|
|
59026
|
+
"projectStart.allRepositories": "All repositories",
|
|
59027
|
+
"projectStart.selectedRepositories": "Selected repositories",
|
|
59028
|
+
"projectStart.lastConnected": "Last connected",
|
|
59029
|
+
"projectStart.patTitle": "Personal repository access token",
|
|
59030
|
+
"projectStart.patIssue": "Open PAT issuance page",
|
|
59031
|
+
"projectStart.patRegistered": "Registered · Personal",
|
|
59032
|
+
"projectStart.patUnregistered": "Not registered",
|
|
59033
|
+
"projectStart.patUpdatedAt": "Last updated",
|
|
59034
|
+
"projectStart.patConfirmDelete": "Confirm delete",
|
|
59035
|
+
"projectStart.patDelete": "Delete",
|
|
59036
|
+
"projectStart.patUpdate": "New PAT (update)",
|
|
59037
|
+
"projectStart.patNew": "New PAT",
|
|
59038
|
+
"projectStart.patPlaceholder": "Enter a token",
|
|
59039
|
+
"projectStart.patHide": "Hide token",
|
|
59040
|
+
"projectStart.patShow": "Show token",
|
|
59041
|
+
"projectStart.patSaveUpdate": "Update",
|
|
59042
|
+
"projectStart.patSaveNew": "Register",
|
|
59043
|
+
"projectStart.patSaved": "Token saved.",
|
|
59044
|
+
"projectStart.patDeleted": "Token deleted.",
|
|
59045
|
+
"projectStart.loadingConnections": "Loading repository connection information…"
|
|
58582
59046
|
};
|
|
58583
59047
|
const agentBrowserConsoleEn = {
|
|
58584
59048
|
"browserPanel.agentBrowserConsole": "AI Browser task console",
|
|
@@ -59149,6 +59613,51 @@ const designGuideEn = {
|
|
|
59149
59613
|
"designGuide.entry.body": "See the color, type, component, and chat standards used to build app screens.",
|
|
59150
59614
|
"designGuide.entry.open": "Open design guide"
|
|
59151
59615
|
};
|
|
59616
|
+
const prFollowThroughEn = {
|
|
59617
|
+
"prFollow.readyTitle": "PR #{number} is ready to merge",
|
|
59618
|
+
"prFollow.readyBody": "CI, the review bot and conflict checks all passed. Merge it when you're ready.",
|
|
59619
|
+
"prFollow.mergeGuard.stacked": "Stacked PRs on this branch are still open: {list}. Merging now leaves them orphaned and they will need to be moved separately.",
|
|
59620
|
+
"prFollow.mergeGuard.botReviewing": "The review bot is still reviewing this PR or preparing a fix PR.",
|
|
59621
|
+
"prFollow.mergeGuard.unknown": "Couldn't check whether stacked PRs remain.",
|
|
59622
|
+
"prFollow.mergeGuard.confirm": "Merge anyway?",
|
|
59623
|
+
"prFollow.mergeGuard.title": "Check before merging",
|
|
59624
|
+
"prFollow.mergeGuard.mergeAnyway": "Merge anyway",
|
|
59625
|
+
"prFollow.enable": "PR watch",
|
|
59626
|
+
"prFollow.enableTitle": "Watches this PR's CI, review bot and conflicts, and tells the conversation what to fix while it is idle. Notifies you when it is ready to merge.",
|
|
59627
|
+
"prFollow.disable": "Stop PR watch",
|
|
59628
|
+
"prFollow.dismiss": "Clear PR watch",
|
|
59629
|
+
"prFollow.badge.prefix": "Watch · {status}",
|
|
59630
|
+
"prFollow.badge.awaitingPr": "waiting for the PR",
|
|
59631
|
+
"prFollow.badge.watching": "watching",
|
|
59632
|
+
"prFollow.badge.checksPending": "checks running",
|
|
59633
|
+
"prFollow.badge.botReviewing": "review bot working",
|
|
59634
|
+
"prFollow.badge.conflict": "conflict",
|
|
59635
|
+
"prFollow.badge.checksFailed": "CI failed",
|
|
59636
|
+
"prFollow.badge.stackedPr": "stacked PR open",
|
|
59637
|
+
"prFollow.badge.botFindings": "bot findings to apply",
|
|
59638
|
+
"prFollow.badge.ready": "ready to merge",
|
|
59639
|
+
"prFollow.badge.merged": "merged",
|
|
59640
|
+
"prFollow.badge.lookupFailed": "couldn't read the PR",
|
|
59641
|
+
"prFollow.badge.stopped.user": "off",
|
|
59642
|
+
"prFollow.badge.stopped.noPr": "no PR was opened",
|
|
59643
|
+
"prFollow.badge.stopped.closed": "PR closed",
|
|
59644
|
+
"prFollow.badge.stopped.budget": "instruction limit reached",
|
|
59645
|
+
"prFollow.badge.stopped.repeatedFailure": "same failure again — needs you",
|
|
59646
|
+
"prFollow.badge.stopped.sessionRemoved": "conversation gone",
|
|
59647
|
+
"prFollow.settings.saveFailed": "Couldn't save the setting.",
|
|
59648
|
+
"prFollow.settings.followThrough": "Start PR watch after Autopilot's Commit & PR",
|
|
59649
|
+
"prFollow.settings.followThroughHelp": "Checks the PR's CI failures, review bot fix PRs and conflicts every minute, and tells the conversation what to fix while it is idle. Sends a notification when the PR is ready to merge. It never merges into the base branch.",
|
|
59650
|
+
"prFollow.settings.continueAfterMerge": "Continue remaining work after merge",
|
|
59651
|
+
"prFollow.settings.continueAfterMergeHelp": "When the PR is merged, tells the same conversation to continue the spec's remaining tasks on a new branch from the latest base, along with handling any leftover stacked PRs.",
|
|
59652
|
+
"prFollow.settings.autoAnswer": "Let Autopilot answer questions that come with a recommendation",
|
|
59653
|
+
"prFollow.settings.autoAnswerHelp": 'During Autopilot, when the AI stops with a recommendation and asks whether to proceed, it answers "go with the recommendation" (up to twice per run). It also tells the AI not to proceed with permissions, costs, external sends or hard-to-undo changes.',
|
|
59654
|
+
"prFollow.settings.reviewer": "Autopilot independent review AI",
|
|
59655
|
+
"prFollow.settings.reviewerDefault": "Default",
|
|
59656
|
+
"prFollow.settings.reviewerModel": "Review model",
|
|
59657
|
+
"prFollow.settings.reviewerEffort": "Review effort",
|
|
59658
|
+
"prFollow.settings.reviewerHelp": "The AI used when Autopilot runs an independent review. The value at the time you turn Autopilot on is kept for that run.",
|
|
59659
|
+
"prFollow.badge.title": "PR watch · {status}"
|
|
59660
|
+
};
|
|
59152
59661
|
const catalogExtensionsEn = {
|
|
59153
59662
|
...chatSchedulesEn,
|
|
59154
59663
|
...mySettingsEn,
|
|
@@ -59229,9 +59738,20 @@ const catalogExtensionsEn = {
|
|
|
59229
59738
|
...lessonCandidateEn,
|
|
59230
59739
|
...gitAccountsEn,
|
|
59231
59740
|
...agentBrowserConsoleEn,
|
|
59232
|
-
...designGuideEn
|
|
59741
|
+
...designGuideEn,
|
|
59742
|
+
...prFollowThroughEn
|
|
59233
59743
|
};
|
|
59234
59744
|
const onboardingEn = {
|
|
59745
|
+
"folderBrowser.title": "Choose folder",
|
|
59746
|
+
"folderBrowser.parent": "Parent folder",
|
|
59747
|
+
"folderBrowser.folders": "Folders",
|
|
59748
|
+
"folderBrowser.loading": "Loading folders…",
|
|
59749
|
+
"folderBrowser.empty": "No subfolders.",
|
|
59750
|
+
"folderBrowser.newPath": "Start from a new path",
|
|
59751
|
+
"folderBrowser.choose": "Select this folder",
|
|
59752
|
+
"projectImport.drop": "Drop a zip, folder or files here, or click to choose",
|
|
59753
|
+
"projectImport.excludeHint": "Heavy folders such as node_modules, .git and dist are excluded automatically",
|
|
59754
|
+
"projectImport.folder": "Choose folder",
|
|
59235
59755
|
"onboarding.page.instructions": "Saycode instructions",
|
|
59236
59756
|
"onboarding.instructions.body": "Saycode’s built-in instructions help the AI organize work and explain results. Choose whether to use them.",
|
|
59237
59757
|
"onboarding.instructions.enable": "Use instructions",
|
|
@@ -59431,7 +59951,8 @@ const onboardingEn = {
|
|
|
59431
59951
|
"onboarding.groups.error.capabilityRequired": "This machine's Saycode CLI does not support organization credentials. Update the CLI, then retry.",
|
|
59432
59952
|
"onboarding.groups.error.setupTokenRuntimeRequired": "This computer is missing the organization setup-token runtime. Install the Saycode cswap runtime, then retry.",
|
|
59433
59953
|
"onboarding.groups.error.happyDaemonUpdateRequired": "This computer's Saycode daemon does not support organization credentials. Update Desktop, then retry.",
|
|
59434
|
-
"onboarding.groups.error.unknown": "The organization credentials could not be connected. Try again shortly."
|
|
59954
|
+
"onboarding.groups.error.unknown": "The organization credentials could not be connected. Try again shortly.",
|
|
59955
|
+
"folderBrowser.path": "Path"
|
|
59435
59956
|
};
|
|
59436
59957
|
const standaloneRemoteOnboardingEn = {
|
|
59437
59958
|
"onboarding.optional": " (optional)",
|
|
@@ -59516,6 +60037,7 @@ const en = {
|
|
|
59516
60037
|
"assetWorkflow.filesSelect": "Read explicitly selected files",
|
|
59517
60038
|
"assetWorkflow.draftsPrepare": "Prepare draft text and attachments in the selected conversation",
|
|
59518
60039
|
...workEn,
|
|
60040
|
+
...workTemplateEn,
|
|
59519
60041
|
...FILE_ACCESS_TRANSLATIONS_EN,
|
|
59520
60042
|
...chatWorkEn,
|
|
59521
60043
|
...usageEntryEn,
|
|
@@ -61327,7 +61849,7 @@ const en = {
|
|
|
61327
61849
|
"saycodePromptPreference.eyebrow": "Choose agent instructions",
|
|
61328
61850
|
"saycodePromptPreference.title": "Use Saycode’s default instructions?",
|
|
61329
61851
|
"saycodePromptPreference.body": "Keep them off to use your own Claude/Codex skills and harnesses as-is. You can change this anytime in Settings.",
|
|
61330
|
-
"saycodePromptPreference.defaultOnNotice": "Child Agent calls, internal task delegation,
|
|
61852
|
+
"saycodePromptPreference.defaultOnNotice": "Child Agent calls, internal task delegation, inline result previews, and the Saycode usage guide remain on by default. To turn any of them off, go to Settings → System Prompt and disable it there.",
|
|
61331
61853
|
"saycodePromptPreference.keepOff": "Keep them off",
|
|
61332
61854
|
"saycodePromptPreference.enable": "Enable Saycode instructions",
|
|
61333
61855
|
"saycodePromptPreference.saving": "Saving…",
|
|
@@ -61336,7 +61858,7 @@ const en = {
|
|
|
61336
61858
|
"saycodePromptPreference.settingsEnable": "Use default behavioral instructions provided by Saycode",
|
|
61337
61859
|
"saycodePromptPreference.settingsHelp": "Your system prompt, project instructions, installed skills, and harnesses remain available regardless of this setting.",
|
|
61338
61860
|
"saycodePromptPreference.blocksTitle": "Individual instructions",
|
|
61339
|
-
"saycodePromptPreference.blocksHelp": "Child Agent calls, internal task delegation,
|
|
61861
|
+
"saycodePromptPreference.blocksHelp": "Child Agent calls, internal task delegation, inline result previews, and the Saycode usage guide are on by default. Other unset items follow the setting above.",
|
|
61340
61862
|
"saycodePromptPreference.alwaysOnNotice": "Chat title generation is always on and cannot be turned off. It is what lets you find a conversation again in the chat list.",
|
|
61341
61863
|
"saycodePromptPreference.block.agentOrchestration.label": "Child Agent calls",
|
|
61342
61864
|
"saycodePromptPreference.block.agentOrchestration.description": "Instructs the agent to delegate natural-language requests to an independent child Agent session and collect its result.",
|
|
@@ -61348,6 +61870,8 @@ const en = {
|
|
|
61348
61870
|
"saycodePromptPreference.block.coAuthoredCredit.description": "Adds the Claude and Happy co-author trailers to commit messages.",
|
|
61349
61871
|
"saycodePromptPreference.block.inlinePreview.label": "Inline result previews",
|
|
61350
61872
|
"saycodePromptPreference.block.inlinePreview.description": "Tells the agent to show HTML deliverables as inline previews in its replies.",
|
|
61873
|
+
"saycodePromptPreference.block.saycodeGuide.label": "Saycode usage guide",
|
|
61874
|
+
"saycodePromptPreference.block.saycodeGuide.description": "When you ask how to use Saycode, the matching guide entries and a few app details (version, usage type, display settings) are sent to this conversation's AI. The full guide is placed in the conversation's working folder under .aplus/saycode-guide and kept out of your repository. Deciding whether it is such a question happens on this device.",
|
|
61351
61875
|
"saycodePromptPreference.loading": "Loading account settings…",
|
|
61352
61876
|
"standalone.zrokStatus": "zrok status",
|
|
61353
61877
|
"standalone.zrokNeeded": "zrok2 setup is required",
|
|
@@ -62646,6 +63170,33 @@ const managedClaudeZh = {
|
|
|
62646
63170
|
"managedClaude.offline": "正在连接工作电脑。",
|
|
62647
63171
|
"managedClaude.loading": "正在连接AI…"
|
|
62648
63172
|
};
|
|
63173
|
+
const workTemplateZh = {
|
|
63174
|
+
"work.gallery": "文档模板",
|
|
63175
|
+
"work.templates": "模板",
|
|
63176
|
+
"work.create": "创建我的模板",
|
|
63177
|
+
"work.closeGallery": "关闭模板列表",
|
|
63178
|
+
"work.edit": "编辑模板",
|
|
63179
|
+
"work.delete": "删除模板",
|
|
63180
|
+
"work.deleteConfirm": "删除模板“{name}”?",
|
|
63181
|
+
"work.description": "保存现有文档的格式,以便在下次Work中使用。",
|
|
63182
|
+
"work.name": "模板名称",
|
|
63183
|
+
"work.file": "源文件",
|
|
63184
|
+
"work.chooseFile": "选择文件",
|
|
63185
|
+
"work.noFileSelected": "未选择文件",
|
|
63186
|
+
"work.preserve": "要保留的格式和编写说明",
|
|
63187
|
+
"work.save": "保存模板",
|
|
63188
|
+
"work.saving": "正在保存…",
|
|
63189
|
+
"work.instructions": "编写说明",
|
|
63190
|
+
"work.saveChanges": "保存更改",
|
|
63191
|
+
"work.templateInvalid": "无法验证所选模板。",
|
|
63192
|
+
"work.templateChanged": "模板已更改,请重新选择。",
|
|
63193
|
+
"work.templatesUnavailable": "无法加载模板列表。",
|
|
63194
|
+
"work.clearTemplate": "取消模板选择",
|
|
63195
|
+
"work.templatesLoading": "正在加载模板…",
|
|
63196
|
+
"work.templateSaveFailed": "无法保存模板,请检查内容后重试。",
|
|
63197
|
+
"work.templatePrompt": "使用 {name} 模板技能创建 {format} 文档,保留原有设计与结构。",
|
|
63198
|
+
"work.taskCue": "我的任务是"
|
|
63199
|
+
};
|
|
62649
63200
|
const workZh = {
|
|
62650
63201
|
"work.mediaChanged": "原文件已更改。请重新打开。",
|
|
62651
63202
|
"work.mediaUnsupported": "此计算机不支持媒体流式播放。请保存原文件并在本地打开。",
|
|
@@ -62849,6 +63400,7 @@ const organizationStartZh = {
|
|
|
62849
63400
|
"organizationStart.welcome": "立即在组织中开始",
|
|
62850
63401
|
"organizationStart.welcomeBody": "我们会自动准备工具并连接这台电脑。",
|
|
62851
63402
|
"organizationStart.chooseTitle": "您要在哪个组织工作?",
|
|
63403
|
+
"organizationStart.resumeVerified": "使用已确认的组织继续",
|
|
62852
63404
|
"organizationStart.next": "下一步",
|
|
62853
63405
|
"organizationStart.switching": "正在连接组织…",
|
|
62854
63406
|
"organizationStart.confirmConnectionTitle": "确认电脑连接",
|
|
@@ -63001,11 +63553,30 @@ const organizationStartZh = {
|
|
|
63001
63553
|
"companyInvite.declineConfirm": "确认拒绝",
|
|
63002
63554
|
"companyInvite.declineBody": "拒绝{name}的邀请?之后加入需要新的邀请。",
|
|
63003
63555
|
"companyInvite.cancel": "取消",
|
|
63004
|
-
"companyInvite.loading": "
|
|
63556
|
+
"companyInvite.loading": "正在检查组织和邀请…",
|
|
63005
63557
|
"companyInvite.acceptLabel": "接受{name}的邀请",
|
|
63006
63558
|
"companyInvite.declineLabel": "拒绝{name}的邀请",
|
|
63007
63559
|
"companyInvite.declineConfirmLabel": "确认拒绝{name}的邀请",
|
|
63008
|
-
"companyInvite.refreshFailed": "
|
|
63560
|
+
"companyInvite.refreshFailed": "无法检查组织和邀请,请重试。",
|
|
63561
|
+
"organizationEnrollment.join": "使用邀请码加入",
|
|
63562
|
+
"organizationEnrollment.create": "创建组织",
|
|
63563
|
+
"organizationEnrollment.code": "邀请码",
|
|
63564
|
+
"organizationEnrollment.name": "名称",
|
|
63565
|
+
"organizationEnrollment.joinSubmit": "加入组织",
|
|
63566
|
+
"organizationEnrollment.createSubmit": "创建",
|
|
63567
|
+
"organizationEnrollment.back": "返回组织选项",
|
|
63568
|
+
"organizationEnrollment.joinHelp": "请输入组织管理员提供的邀请码。",
|
|
63569
|
+
"organizationEnrollment.working": "正在确认组织…",
|
|
63570
|
+
"organizationEnrollment.joined": "已确认组织。请选择下一步继续准备。",
|
|
63571
|
+
"organizationEnrollment.unconfirmed": "尚未确认处理结果。请重新检查组织列表而不重复提交,然后选择已加入的组织。",
|
|
63572
|
+
"organizationEnrollment.recheck": "重新检查组织列表",
|
|
63573
|
+
"organizationEnrollment.invalidCode": "请检查邀请码。如已过期或撤销,请联系管理员重新获取。",
|
|
63574
|
+
"organizationEnrollment.slugTaken": "无法准备组织地址,请重试。",
|
|
63575
|
+
"organizationEnrollment.rateLimited": "请求过多,请稍后重试。",
|
|
63576
|
+
"organizationEnrollment.signInAgain": "登录已过期,请重新登录后加入。",
|
|
63577
|
+
"organizationEnrollment.failed": "无法处理请求。请检查输入并重试。",
|
|
63578
|
+
"organizationEnrollment.welcomeBody": "请选择要一起工作的组织,或创建新组织。",
|
|
63579
|
+
"organizationEnrollment.other": "选择或加入其他组织"
|
|
63009
63580
|
};
|
|
63010
63581
|
const usageEntryZh = {
|
|
63011
63582
|
...selfHostConnectZh,
|
|
@@ -63149,7 +63720,38 @@ const usageEntryZh = {
|
|
|
63149
63720
|
"onboarding.additionalStep": "附加步骤",
|
|
63150
63721
|
"onboarding.requiredSettings": "必需设置",
|
|
63151
63722
|
"onboarding.additionalSettings": "附加设置 — 可选",
|
|
63152
|
-
"onboarding.cli.bundledPreparation": "Saycode CLI 已内置。准备必需工具后,注册此计算机并验证守护进程。"
|
|
63723
|
+
"onboarding.cli.bundledPreparation": "Saycode CLI 已内置。准备必需工具后,注册此计算机并验证守护进程。",
|
|
63724
|
+
"signup.open": "创建账户",
|
|
63725
|
+
"signup.title": "创建账户",
|
|
63726
|
+
"signup.sendCode": "发送验证码",
|
|
63727
|
+
"signup.sendingCode": "正在发送验证码…",
|
|
63728
|
+
"signup.code": "请输入邮件中收到的验证码",
|
|
63729
|
+
"signup.resend": "重新发送",
|
|
63730
|
+
"signup.password": "密码",
|
|
63731
|
+
"signup.confirmation": "确认密码",
|
|
63732
|
+
"signup.minimum": "至少12个字符",
|
|
63733
|
+
"signup.mismatch": "两次密码不一致。",
|
|
63734
|
+
"signup.submit": "注册并开始",
|
|
63735
|
+
"signup.creating": "正在创建账户…",
|
|
63736
|
+
"signup.back": "登录",
|
|
63737
|
+
"signup.registered": "此邮箱已注册,请登录。",
|
|
63738
|
+
"signup.rateLimited": "请求过多,请稍后重试。",
|
|
63739
|
+
"signup.requestFailed": "无法请求验证码,请检查连接后重试。",
|
|
63740
|
+
"signup.invalidCode": "验证码无效或已过期,请重新确认。",
|
|
63741
|
+
"signup.recoveryUnavailable": "账户恢复暂不可用,请稍后重试。",
|
|
63742
|
+
"signup.failed": "无法完成注册,请检查输入后重试。",
|
|
63743
|
+
"signup.unconfirmed": "无法确认注册结果,请登录确认账户。",
|
|
63744
|
+
"signup.complete": "注册已完成,请登录开始。",
|
|
63745
|
+
"signup.continueLogin": "登录并开始",
|
|
63746
|
+
"signup.terms": "继续即表示您同意服务条款。",
|
|
63747
|
+
"signup.showPassword": "显示密码",
|
|
63748
|
+
"signup.hidePassword": "隐藏密码",
|
|
63749
|
+
"signup.enterCode": "输入验证码",
|
|
63750
|
+
"signup.digit": "验证码第 {number} 位",
|
|
63751
|
+
"signup.verify": "验证并继续",
|
|
63752
|
+
"signup.verifying": "正在验证…",
|
|
63753
|
+
"signup.previous": "上一步",
|
|
63754
|
+
"signup.verifyFailed": "无法验证验证码。请检查网络连接后重试。"
|
|
63153
63755
|
};
|
|
63154
63756
|
const homeTabsZh = {
|
|
63155
63757
|
"homeTabs.collaboratingTitle": "协作项目",
|
|
@@ -63207,7 +63809,7 @@ const homeTabsZh = {
|
|
|
63207
63809
|
"homeTabs.startingWork": "正在开始工作",
|
|
63208
63810
|
"homeTabs.startWorkFailed": "无法开始工作。",
|
|
63209
63811
|
"homeTabs.syncedBeforeImplementation": "在开始实施前同步工作区计划",
|
|
63210
|
-
"homeTabs.quick": "
|
|
63812
|
+
"homeTabs.quick": "创建项目",
|
|
63211
63813
|
"homeTabs.git": "导入代码仓库",
|
|
63212
63814
|
"homeTabs.zip": "导入 ZIP/文件",
|
|
63213
63815
|
"homeTabs.folder": "使用机器文件夹",
|
|
@@ -63511,7 +64113,15 @@ const workAreaZh = {
|
|
|
63511
64113
|
"workArea.panelFailed": "无法显示此界面",
|
|
63512
64114
|
"workArea.panelFailedHint": "如果重试后仍然如此,请关闭此标签页并从列表中重新打开。对话和工作内容都会保留。",
|
|
63513
64115
|
"workArea.panelRetry": "重试",
|
|
63514
|
-
"workArea.panelClose": "关闭标签页"
|
|
64116
|
+
"workArea.panelClose": "关闭标签页",
|
|
64117
|
+
"workArea.nameSearch.hint": "筛选列表 · 按 Enter 搜索所有文件名",
|
|
64118
|
+
"workArea.nameSearch.loading": "正在查找项目文件名…",
|
|
64119
|
+
"workArea.nameSearch.partial": "仅确认部分文件 · 按 Enter 重新搜索",
|
|
64120
|
+
"workArea.nameSearch.results": "项目搜索结果 · 按 Esc 清除",
|
|
64121
|
+
"workArea.nameSearch.empty": "没有匹配的文件 · 按 Esc 清除",
|
|
64122
|
+
"workArea.revealFile": "在文件列表中显示位置",
|
|
64123
|
+
"workArea.revealFailed": "无法确认文件位置。请再次点击显示位置。",
|
|
64124
|
+
"workArea.fileListFailed": "无法加载文件列表。"
|
|
63515
64125
|
};
|
|
63516
64126
|
const redesignZh = {
|
|
63517
64127
|
"newSession.aiAuto": "AI 自动",
|
|
@@ -64659,7 +65269,7 @@ const personalChatsZh = {
|
|
|
64659
65269
|
"personalChats.creating": "正在开始对话…",
|
|
64660
65270
|
"personalChats.noOnlineMachine": "没有可用的在线机器。请启动一台机器来开始对话。",
|
|
64661
65271
|
"personalChats.localMachinePreparing": "正在将这台电脑连接到 Saycode,稍后即可开始。",
|
|
64662
|
-
"personalChats.localMachineAsk": "
|
|
65272
|
+
"personalChats.localMachineAsk": "请将这台电脑连接为工作电脑以开始聊天。",
|
|
64663
65273
|
"personalChats.localMachineFailed": "这台电脑自动连接失败。可以用其他方式连接。",
|
|
64664
65274
|
"personalChats.pickMachine": "选择用于此对话的机器",
|
|
64665
65275
|
"personalChats.createFailed": "无法开始对话。",
|
|
@@ -66049,7 +66659,24 @@ const organizationTeamsZh = {
|
|
|
66049
66659
|
"organizationTeams.shareApply": "应用"
|
|
66050
66660
|
};
|
|
66051
66661
|
const gitAccountsZh = {
|
|
66052
|
-
"
|
|
66662
|
+
"projectStart.url": "从地址导入",
|
|
66663
|
+
"projectStart.clearRepository": "清除仓库选择",
|
|
66664
|
+
"projectStart.providerPicker": "选择 {provider} 仓库",
|
|
66665
|
+
"projectStart.manageAccess": "管理仓库访问权限",
|
|
66666
|
+
"projectStart.repositoryPlaceholder": "请选择仓库",
|
|
66667
|
+
"projectStart.localAccount": "此电脑的 GitHub 账号 · @{account}",
|
|
66668
|
+
"projectStart.localGit": "此电脑的 Git 配置",
|
|
66669
|
+
"projectStart.machineGit": "工作电脑的 Git 配置",
|
|
66670
|
+
"projectStart.githubAccount": "已连接的 GitHub 账号",
|
|
66671
|
+
"projectStart.gitlabAccount": "已连接的 GitLab 账号",
|
|
66672
|
+
"projectStart.organizationPat": "组织 PAT · {name}",
|
|
66673
|
+
"projectStart.personalPat": "个人 PAT",
|
|
66674
|
+
"projectStart.selectedConnectionHint": "使用所选仓库的连接账号。",
|
|
66675
|
+
"projectStart.localAccountHint": "使用此电脑登录的 GitHub 账号导入。",
|
|
66676
|
+
"projectStart.machineGitHint": "使用工作电脑配置的 Git 认证。",
|
|
66677
|
+
"projectStart.accountHint": "使用所选连接账号访问仓库。",
|
|
66678
|
+
"projectStart.connectionUnavailable": "所选仓库连接不可用,请检查连接状态。",
|
|
66679
|
+
"settingsTab.gitAccounts": "代码仓库",
|
|
66053
66680
|
"gitAccounts.title": "GitHub · GitLab 账号",
|
|
66054
66681
|
"gitAccounts.description": "连接用于导入仓库、触发器和 PR 工作的账号。令牌仅保存在服务器上,不会存储在本设备。",
|
|
66055
66682
|
"gitAccounts.refresh": "刷新状态",
|
|
@@ -66065,7 +66692,27 @@ const gitAccountsZh = {
|
|
|
66065
66692
|
"gitAccounts.connectFailed": "无法开始连接,请稍后重试。",
|
|
66066
66693
|
"gitAccounts.disconnectFailed": "无法断开连接,请稍后重试。",
|
|
66067
66694
|
"gitAccounts.warnings": "已断开,但有需要确认的项目:",
|
|
66068
|
-
"
|
|
66695
|
+
"projectStart.repositorySettings": "仓库设置",
|
|
66696
|
+
"projectStart.allRepositories": "所有仓库",
|
|
66697
|
+
"projectStart.selectedRepositories": "选定仓库",
|
|
66698
|
+
"projectStart.lastConnected": "上次连接",
|
|
66699
|
+
"projectStart.patTitle": "个人代码仓库 Personal Access Token",
|
|
66700
|
+
"projectStart.patIssue": "打开PAT签发页面",
|
|
66701
|
+
"projectStart.patRegistered": "已注册 · 个人",
|
|
66702
|
+
"projectStart.patUnregistered": "未注册",
|
|
66703
|
+
"projectStart.patUpdatedAt": "上次更新",
|
|
66704
|
+
"projectStart.patConfirmDelete": "确认删除",
|
|
66705
|
+
"projectStart.patDelete": "删除",
|
|
66706
|
+
"projectStart.patUpdate": "新PAT(更新)",
|
|
66707
|
+
"projectStart.patNew": "新PAT",
|
|
66708
|
+
"projectStart.patPlaceholder": "输入令牌",
|
|
66709
|
+
"projectStart.patHide": "隐藏令牌",
|
|
66710
|
+
"projectStart.patShow": "显示令牌",
|
|
66711
|
+
"projectStart.patSaveUpdate": "更新",
|
|
66712
|
+
"projectStart.patSaveNew": "注册",
|
|
66713
|
+
"projectStart.patSaved": "令牌已保存。",
|
|
66714
|
+
"projectStart.patDeleted": "令牌已删除。",
|
|
66715
|
+
"projectStart.loadingConnections": "正在加载仓库连接信息…"
|
|
66069
66716
|
};
|
|
66070
66717
|
const agentBrowserConsoleZh = {
|
|
66071
66718
|
"browserPanel.agentBrowserConsole": "AI 浏览器任务控制台",
|
|
@@ -66636,6 +67283,51 @@ const designGuideZh = {
|
|
|
66636
67283
|
"designGuide.entry.body": "查看构建应用界面时使用的颜色、文字、组件和聊天界面标准。",
|
|
66637
67284
|
"designGuide.entry.open": "打开设计指南"
|
|
66638
67285
|
};
|
|
67286
|
+
const prFollowThroughZh = {
|
|
67287
|
+
"prFollow.readyTitle": "PR #{number} 可以合并",
|
|
67288
|
+
"prFollow.readyBody": "CI、审查机器人和冲突检查均已通过。请自行合并。",
|
|
67289
|
+
"prFollow.mergeGuard.stacked": "此 PR 分支上仍有未合入的堆叠 PR:{list}。现在合并会使它们成为孤立 PR,需要另行迁移。",
|
|
67290
|
+
"prFollow.mergeGuard.botReviewing": "审查机器人仍在审查此 PR 或准备修复 PR。",
|
|
67291
|
+
"prFollow.mergeGuard.unknown": "无法确认是否还有堆叠 PR。",
|
|
67292
|
+
"prFollow.mergeGuard.confirm": "仍要合并吗?",
|
|
67293
|
+
"prFollow.mergeGuard.title": "合并前请确认",
|
|
67294
|
+
"prFollow.mergeGuard.mergeAnyway": "仍然合并",
|
|
67295
|
+
"prFollow.enable": "PR 守护",
|
|
67296
|
+
"prFollow.enableTitle": "监视此 PR 的 CI、审查机器人和冲突,在对话空闲时代为下达修复指示。可以合并时通知你。",
|
|
67297
|
+
"prFollow.disable": "停止 PR 守护",
|
|
67298
|
+
"prFollow.dismiss": "清除 PR 守护记录",
|
|
67299
|
+
"prFollow.badge.prefix": "守护 · {status}",
|
|
67300
|
+
"prFollow.badge.awaitingPr": "等待 PR",
|
|
67301
|
+
"prFollow.badge.watching": "监视中",
|
|
67302
|
+
"prFollow.badge.checksPending": "等待检查",
|
|
67303
|
+
"prFollow.badge.botReviewing": "等待审查机器人",
|
|
67304
|
+
"prFollow.badge.conflict": "冲突",
|
|
67305
|
+
"prFollow.badge.checksFailed": "CI 失败",
|
|
67306
|
+
"prFollow.badge.stackedPr": "存在堆叠 PR",
|
|
67307
|
+
"prFollow.badge.botFindings": "需处理机器人意见",
|
|
67308
|
+
"prFollow.badge.ready": "可以合并",
|
|
67309
|
+
"prFollow.badge.merged": "已合并",
|
|
67310
|
+
"prFollow.badge.lookupFailed": "无法读取 PR 状态",
|
|
67311
|
+
"prFollow.badge.stopped.user": "已关闭",
|
|
67312
|
+
"prFollow.badge.stopped.noPr": "未创建 PR",
|
|
67313
|
+
"prFollow.badge.stopped.closed": "PR 已关闭",
|
|
67314
|
+
"prFollow.badge.stopped.budget": "已达自动指示上限",
|
|
67315
|
+
"prFollow.badge.stopped.repeatedFailure": "同样的失败反复出现 — 需要你处理",
|
|
67316
|
+
"prFollow.badge.stopped.sessionRemoved": "对话已不存在",
|
|
67317
|
+
"prFollow.settings.saveFailed": "无法保存设置。",
|
|
67318
|
+
"prFollow.settings.followThrough": "自动驾驶 Commit & PR 后启动 PR 守护",
|
|
67319
|
+
"prFollow.settings.followThroughHelp": "每分钟检查 PR 的 CI 失败、审查机器人修复 PR 和冲突,在对话空闲时代为下达修复指示。可以合并时发送通知。不会合并到 base 分支。",
|
|
67320
|
+
"prFollow.settings.continueAfterMerge": "合并后继续剩余工作",
|
|
67321
|
+
"prFollow.settings.continueAfterMergeHelp": "PR 合并后,指示同一对话基于最新 base 在新分支上继续 spec 的剩余任务,如有遗留的堆叠 PR 也一并处理。",
|
|
67322
|
+
"prFollow.settings.autoAnswer": "让自动驾驶代为回答附带推荐方案的问题",
|
|
67323
|
+
"prFollow.settings.autoAnswerHelp": "自动驾驶期间,AI 给出推荐方案并停下询问是否继续时,会代为回答“按推荐方案进行”(每次最多 2 次),同时要求不要执行权限、费用、外部发送或难以撤销的更改。",
|
|
67324
|
+
"prFollow.settings.reviewer": "自动驾驶独立审查 AI",
|
|
67325
|
+
"prFollow.settings.reviewerDefault": "默认",
|
|
67326
|
+
"prFollow.settings.reviewerModel": "审查模型",
|
|
67327
|
+
"prFollow.settings.reviewerEffort": "审查 Effort",
|
|
67328
|
+
"prFollow.settings.reviewerHelp": "以独立审查模式启用自动驾驶时使用的 AI。启用时的设置会固定用于该次运行。",
|
|
67329
|
+
"prFollow.badge.title": "PR 守护 · {status}"
|
|
67330
|
+
};
|
|
66639
67331
|
const catalogExtensionsZh = {
|
|
66640
67332
|
...chatSchedulesZh,
|
|
66641
67333
|
...mySettingsZh,
|
|
@@ -66716,9 +67408,20 @@ const catalogExtensionsZh = {
|
|
|
66716
67408
|
...lessonCandidateZh,
|
|
66717
67409
|
...gitAccountsZh,
|
|
66718
67410
|
...agentBrowserConsoleZh,
|
|
66719
|
-
...designGuideZh
|
|
67411
|
+
...designGuideZh,
|
|
67412
|
+
...prFollowThroughZh
|
|
66720
67413
|
};
|
|
66721
67414
|
const onboardingZh = {
|
|
67415
|
+
"folderBrowser.title": "选择文件夹",
|
|
67416
|
+
"folderBrowser.parent": "上级文件夹",
|
|
67417
|
+
"folderBrowser.folders": "文件夹列表",
|
|
67418
|
+
"folderBrowser.loading": "正在读取文件夹…",
|
|
67419
|
+
"folderBrowser.empty": "没有子文件夹。",
|
|
67420
|
+
"folderBrowser.newPath": "从新路径开始",
|
|
67421
|
+
"folderBrowser.choose": "选择此文件夹",
|
|
67422
|
+
"projectImport.drop": "拖入 zip、文件夹或文件,或点击选择",
|
|
67423
|
+
"projectImport.excludeHint": "自动排除 node_modules / .git / dist 等大型文件夹",
|
|
67424
|
+
"projectImport.folder": "选择文件夹",
|
|
66722
67425
|
"onboarding.page.instructions": "Saycode 指令",
|
|
66723
67426
|
"onboarding.instructions.body": "Saycode 的默认指令帮助 AI 整理工作并说明结果。请选择是否使用。",
|
|
66724
67427
|
"onboarding.instructions.enable": "使用指令",
|
|
@@ -66918,7 +67621,8 @@ const onboardingZh = {
|
|
|
66918
67621
|
"onboarding.groups.error.capabilityRequired": "这台机器的 Saycode CLI 不支持组织认证信息。请更新 CLI 后重试。",
|
|
66919
67622
|
"onboarding.groups.error.setupTokenRuntimeRequired": "这台电脑缺少组织 setup-token runtime。请安装 Saycode cswap runtime 后重试。",
|
|
66920
67623
|
"onboarding.groups.error.happyDaemonUpdateRequired": "这台电脑上的 Saycode 守护进程不支持组织认证信息。请更新 Desktop 后重试。",
|
|
66921
|
-
"onboarding.groups.error.unknown": "无法连接组织认证信息,请稍后重试。"
|
|
67624
|
+
"onboarding.groups.error.unknown": "无法连接组织认证信息,请稍后重试。",
|
|
67625
|
+
"folderBrowser.path": "路径"
|
|
66922
67626
|
};
|
|
66923
67627
|
const standaloneRemoteOnboardingZh = {
|
|
66924
67628
|
"onboarding.optional": "(可选)",
|
|
@@ -67003,6 +67707,7 @@ const zh = {
|
|
|
67003
67707
|
"assetWorkflow.filesSelect": "读取明确选择的文件",
|
|
67004
67708
|
"assetWorkflow.draftsPrepare": "准备所选会话的草稿与附件",
|
|
67005
67709
|
...workZh,
|
|
67710
|
+
...workTemplateZh,
|
|
67006
67711
|
...FILE_ACCESS_TRANSLATIONS_ZH,
|
|
67007
67712
|
...chatWorkZh,
|
|
67008
67713
|
...usageEntryZh,
|
|
@@ -68781,7 +69486,7 @@ const zh = {
|
|
|
68781
69486
|
"saycodePromptPreference.eyebrow": "选择智能体指令",
|
|
68782
69487
|
"saycodePromptPreference.title": "要使用 Saycode 默认指令吗?",
|
|
68783
69488
|
"saycodePromptPreference.body": "如果要原样使用自己的 Claude/Codex 技能和工作流,请保持关闭。之后可随时在设置中更改。",
|
|
68784
|
-
"saycodePromptPreference.defaultOnNotice": "子 Agent
|
|
69489
|
+
"saycodePromptPreference.defaultOnNotice": "子 Agent 调用、内部任务委派、结果内联预览和 Saycode 使用指南默认保持开启。如需关闭任一功能,请前往设置 → 系统提示并单独禁用。",
|
|
68785
69490
|
"saycodePromptPreference.keepOff": "保持关闭",
|
|
68786
69491
|
"saycodePromptPreference.enable": "开启 Saycode 指令",
|
|
68787
69492
|
"saycodePromptPreference.saving": "正在保存…",
|
|
@@ -68790,7 +69495,7 @@ const zh = {
|
|
|
68790
69495
|
"saycodePromptPreference.settingsEnable": "使用 Saycode 提供的默认行为指令",
|
|
68791
69496
|
"saycodePromptPreference.settingsHelp": "无论此设置如何,您的系统提示、项目指令、已安装技能和工作流都会保留。",
|
|
68792
69497
|
"saycodePromptPreference.blocksTitle": "单项指令",
|
|
68793
|
-
"saycodePromptPreference.blocksHelp": "子 Agent
|
|
69498
|
+
"saycodePromptPreference.blocksHelp": "子 Agent 调用、内部任务委派、结果内联预览和 Saycode 使用指南默认开启。其他未设置项目将遵循上方的总开关。",
|
|
68794
69499
|
"saycodePromptPreference.alwaysOnNotice": "聊天标题生成始终开启且无法关闭。它让您能在聊天列表中重新找到对话。",
|
|
68795
69500
|
"saycodePromptPreference.block.agentOrchestration.label": "子 Agent 调用",
|
|
68796
69501
|
"saycodePromptPreference.block.agentOrchestration.description": "指示代理将自然语言请求委派给独立的子 Agent 会话并收集结果。",
|
|
@@ -68802,6 +69507,8 @@ const zh = {
|
|
|
68802
69507
|
"saycodePromptPreference.block.coAuthoredCredit.description": "在提交信息中添加 Claude 与 Happy 的共同作者署名。",
|
|
68803
69508
|
"saycodePromptPreference.block.inlinePreview.label": "结果内联预览",
|
|
68804
69509
|
"saycodePromptPreference.block.inlinePreview.description": "指示智能体在回复中以内联预览展示 HTML 成果。",
|
|
69510
|
+
"saycodePromptPreference.block.saycodeGuide.label": "Saycode 使用指南",
|
|
69511
|
+
"saycodePromptPreference.block.saycodeGuide.description": "当你询问如何使用 Saycode 时,会把相关指南和部分应用状态(版本、使用方式、显示设置)发送给本次对话的 AI。完整指南放在对话工作文件夹的 .aplus/saycode-guide 中,不会进入你的仓库。是否属于此类问题只在本设备上判断。",
|
|
68805
69512
|
"saycodePromptPreference.loading": "正在加载账户设置…",
|
|
68806
69513
|
"standalone.zrokStatus": "zrok 状态",
|
|
68807
69514
|
"standalone.zrokNeeded": "需要设置 zrok2",
|
|
@@ -70133,6 +70840,33 @@ const managedClaudeJa = {
|
|
|
70133
70840
|
"managedClaude.offline": "作業PCに接続中です。",
|
|
70134
70841
|
"managedClaude.loading": "AIに接続中…"
|
|
70135
70842
|
};
|
|
70843
|
+
const workTemplateJa = {
|
|
70844
|
+
"work.gallery": "文書テンプレート",
|
|
70845
|
+
"work.templates": "テンプレート",
|
|
70846
|
+
"work.create": "マイテンプレートを作成",
|
|
70847
|
+
"work.closeGallery": "テンプレート一覧を閉じる",
|
|
70848
|
+
"work.edit": "テンプレートを編集",
|
|
70849
|
+
"work.delete": "テンプレートを削除",
|
|
70850
|
+
"work.deleteConfirm": "テンプレート「{name}」を削除しますか?",
|
|
70851
|
+
"work.description": "既存の文書の書式を保存して、次のWorkで再利用できます。",
|
|
70852
|
+
"work.name": "テンプレート名",
|
|
70853
|
+
"work.file": "元のファイル",
|
|
70854
|
+
"work.chooseFile": "ファイルを選択",
|
|
70855
|
+
"work.noFileSelected": "ファイル未選択",
|
|
70856
|
+
"work.preserve": "保持する書式と作成指示",
|
|
70857
|
+
"work.save": "テンプレートを保存",
|
|
70858
|
+
"work.saving": "保存中…",
|
|
70859
|
+
"work.instructions": "作成指示",
|
|
70860
|
+
"work.saveChanges": "変更を保存",
|
|
70861
|
+
"work.templateInvalid": "選択したテンプレートを確認できません。",
|
|
70862
|
+
"work.templateChanged": "テンプレートが変更されました。再度選択してください。",
|
|
70863
|
+
"work.templatesUnavailable": "テンプレートを読み込めませんでした。",
|
|
70864
|
+
"work.clearTemplate": "テンプレートの選択を解除",
|
|
70865
|
+
"work.templatesLoading": "テンプレートを読み込み中…",
|
|
70866
|
+
"work.templateSaveFailed": "保存できませんでした。内容を確認して再試行してください。",
|
|
70867
|
+
"work.templatePrompt": "{name} テンプレートスキルで {format} 文書を作成してください。元のデザインと構造を維持してください。",
|
|
70868
|
+
"work.taskCue": "作業内容は"
|
|
70869
|
+
};
|
|
70136
70870
|
const workJa = {
|
|
70137
70871
|
"work.mediaChanged": "元のファイルが変更されました。もう一度開いてください。",
|
|
70138
70872
|
"work.mediaUnsupported": "このコンピューターではメディアストリーミングを利用できません。元のファイルを保存して開いてください。",
|
|
@@ -70336,6 +71070,7 @@ const organizationStartJa = {
|
|
|
70336
71070
|
"organizationStart.welcome": "組織ですぐに始めましょう",
|
|
70337
71071
|
"organizationStart.welcomeBody": "必要なツールとこのコンピュータの接続を自動で準備します。",
|
|
70338
71072
|
"organizationStart.chooseTitle": "どの組織で作業しますか?",
|
|
71073
|
+
"organizationStart.resumeVerified": "確認済みの組織で続ける",
|
|
70339
71074
|
"organizationStart.next": "次へ",
|
|
70340
71075
|
"organizationStart.switching": "組織に接続中…",
|
|
70341
71076
|
"organizationStart.confirmConnectionTitle": "PC接続の確認",
|
|
@@ -70488,11 +71223,30 @@ const organizationStartJa = {
|
|
|
70488
71223
|
"companyInvite.declineConfirm": "辞退を確認",
|
|
70489
71224
|
"companyInvite.declineBody": "{name}からの招待を辞退しますか?後から参加するには新しい招待が必要です。",
|
|
70490
71225
|
"companyInvite.cancel": "キャンセル",
|
|
70491
|
-
"companyInvite.loading": "
|
|
71226
|
+
"companyInvite.loading": "組織と招待を確認中…",
|
|
70492
71227
|
"companyInvite.acceptLabel": "{name}の招待を承認",
|
|
70493
71228
|
"companyInvite.declineLabel": "{name}の招待を辞退",
|
|
70494
71229
|
"companyInvite.declineConfirmLabel": "{name}の招待の辞退を確認",
|
|
70495
|
-
"companyInvite.refreshFailed": "
|
|
71230
|
+
"companyInvite.refreshFailed": "組織と招待を確認できませんでした。もう一度お試しください。",
|
|
71231
|
+
"organizationEnrollment.join": "招待コードで参加",
|
|
71232
|
+
"organizationEnrollment.create": "組織を作成",
|
|
71233
|
+
"organizationEnrollment.code": "招待コード",
|
|
71234
|
+
"organizationEnrollment.name": "名前",
|
|
71235
|
+
"organizationEnrollment.joinSubmit": "組織に参加",
|
|
71236
|
+
"organizationEnrollment.createSubmit": "作成",
|
|
71237
|
+
"organizationEnrollment.back": "組織の選択肢に戻る",
|
|
71238
|
+
"organizationEnrollment.joinHelp": "組織の管理者から受け取った招待コードを入力してください。",
|
|
71239
|
+
"organizationEnrollment.working": "組織情報を確認中…",
|
|
71240
|
+
"organizationEnrollment.joined": "組織を確認しました。「次へ」で準備を続けてください。",
|
|
71241
|
+
"organizationEnrollment.unconfirmed": "処理結果をまだ確認できません。リクエストを繰り返さず組織一覧を再確認し、参加した組織を選択してください。",
|
|
71242
|
+
"organizationEnrollment.recheck": "組織一覧を再確認",
|
|
71243
|
+
"organizationEnrollment.invalidCode": "招待コードを確認してください。期限切れや取り消しの場合は管理者に再発行を依頼してください。",
|
|
71244
|
+
"organizationEnrollment.slugTaken": "組織アドレスを準備できませんでした。もう一度お試しください。",
|
|
71245
|
+
"organizationEnrollment.rateLimited": "リクエストが多すぎます。しばらくしてから再試行してください。",
|
|
71246
|
+
"organizationEnrollment.signInAgain": "ログインの有効期限が切れました。再ログインして参加してください。",
|
|
71247
|
+
"organizationEnrollment.failed": "処理できませんでした。入力内容を確認して再試行してください。",
|
|
71248
|
+
"organizationEnrollment.welcomeBody": "一緒に作業する組織を選ぶか、新しく作成してください。",
|
|
71249
|
+
"organizationEnrollment.other": "別の組織を選択・参加"
|
|
70496
71250
|
};
|
|
70497
71251
|
const usageEntryJa = {
|
|
70498
71252
|
...selfHostConnectJa,
|
|
@@ -70636,7 +71390,38 @@ const usageEntryJa = {
|
|
|
70636
71390
|
"onboarding.additionalStep": "追加ステップ",
|
|
70637
71391
|
"onboarding.requiredSettings": "必須設定",
|
|
70638
71392
|
"onboarding.additionalSettings": "追加設定 — 任意",
|
|
70639
|
-
"onboarding.cli.bundledPreparation": "Saycode CLI は同梱されています。必須ツールを準備してから、このコンピューターを登録してデーモンを確認します。"
|
|
71393
|
+
"onboarding.cli.bundledPreparation": "Saycode CLI は同梱されています。必須ツールを準備してから、このコンピューターを登録してデーモンを確認します。",
|
|
71394
|
+
"signup.open": "アカウントを作成",
|
|
71395
|
+
"signup.title": "アカウントを作成",
|
|
71396
|
+
"signup.sendCode": "認証コードを送信",
|
|
71397
|
+
"signup.sendingCode": "コードを送信中…",
|
|
71398
|
+
"signup.code": "メールで届いた認証コードを入力してください",
|
|
71399
|
+
"signup.resend": "再送信",
|
|
71400
|
+
"signup.password": "パスワード",
|
|
71401
|
+
"signup.confirmation": "パスワードを確認",
|
|
71402
|
+
"signup.minimum": "12文字以上",
|
|
71403
|
+
"signup.mismatch": "パスワードが一致しません。",
|
|
71404
|
+
"signup.submit": "登録して開始",
|
|
71405
|
+
"signup.creating": "アカウントを作成中…",
|
|
71406
|
+
"signup.back": "ログイン",
|
|
71407
|
+
"signup.registered": "登録済みのメールです。ログインしてください。",
|
|
71408
|
+
"signup.rateLimited": "リクエストが多すぎます。しばらくしてから再試行してください。",
|
|
71409
|
+
"signup.requestFailed": "コードを送信できませんでした。接続を確認して再試行してください。",
|
|
71410
|
+
"signup.invalidCode": "コードが正しくないか有効期限が切れています。確認してください。",
|
|
71411
|
+
"signup.recoveryUnavailable": "アカウント復旧を利用できません。しばらくしてから再試行してください。",
|
|
71412
|
+
"signup.failed": "登録できませんでした。入力内容を確認して再試行してください。",
|
|
71413
|
+
"signup.unconfirmed": "登録結果を確認できません。ログインして確認してください。",
|
|
71414
|
+
"signup.complete": "登録が完了しました。ログインして開始してください。",
|
|
71415
|
+
"signup.continueLogin": "ログインして開始",
|
|
71416
|
+
"signup.terms": "続行すると、利用規約に同意したものとみなされます。",
|
|
71417
|
+
"signup.showPassword": "パスワードを表示",
|
|
71418
|
+
"signup.hidePassword": "パスワードを隠す",
|
|
71419
|
+
"signup.enterCode": "認証コードを入力",
|
|
71420
|
+
"signup.digit": "認証コードの{number}桁目",
|
|
71421
|
+
"signup.verify": "認証して次へ",
|
|
71422
|
+
"signup.verifying": "認証を確認中…",
|
|
71423
|
+
"signup.previous": "戻る",
|
|
71424
|
+
"signup.verifyFailed": "認証を確認できませんでした。接続を確認して再試行してください。"
|
|
70640
71425
|
};
|
|
70641
71426
|
const homeTabsJa = {
|
|
70642
71427
|
"homeTabs.collaboratingTitle": "共同作業プロジェクト",
|
|
@@ -70694,7 +71479,7 @@ const homeTabsJa = {
|
|
|
70694
71479
|
"homeTabs.startingWork": "作業を開始中",
|
|
70695
71480
|
"homeTabs.startWorkFailed": "作業を開始できませんでした。",
|
|
70696
71481
|
"homeTabs.syncedBeforeImplementation": "実装開始前にワークスペース計画を同期",
|
|
70697
|
-
"homeTabs.quick": "
|
|
71482
|
+
"homeTabs.quick": "プロジェクトを作成",
|
|
70698
71483
|
"homeTabs.git": "リポジトリをインポート",
|
|
70699
71484
|
"homeTabs.zip": "ZIP/ファイルをインポート",
|
|
70700
71485
|
"homeTabs.folder": "マシンのフォルダーを使用",
|
|
@@ -70998,7 +71783,15 @@ const workAreaJa = {
|
|
|
70998
71783
|
"workArea.panelFailed": "この画面を表示できませんでした",
|
|
70999
71784
|
"workArea.panelFailedHint": "再試行しても変わらない場合は、このタブを閉じて一覧から開き直してください。会話と作業内容はそのまま残っています。",
|
|
71000
71785
|
"workArea.panelRetry": "再試行",
|
|
71001
|
-
"workArea.panelClose": "タブを閉じる"
|
|
71786
|
+
"workArea.panelClose": "タブを閉じる",
|
|
71787
|
+
"workArea.nameSearch.hint": "一覧を絞り込み · Enterで全体の名前を検索",
|
|
71788
|
+
"workArea.nameSearch.loading": "プロジェクトのファイル名を検索中…",
|
|
71789
|
+
"workArea.nameSearch.partial": "一部のファイルのみ確認 · Enterで再検索",
|
|
71790
|
+
"workArea.nameSearch.results": "プロジェクトの検索結果 · Escで解除",
|
|
71791
|
+
"workArea.nameSearch.empty": "一致するファイルなし · Escで解除",
|
|
71792
|
+
"workArea.revealFile": "ファイル一覧で場所を表示",
|
|
71793
|
+
"workArea.revealFailed": "ファイルの場所を確認できませんでした。もう一度場所を表示してください。",
|
|
71794
|
+
"workArea.fileListFailed": "一覧を読み込めませんでした。"
|
|
71002
71795
|
};
|
|
71003
71796
|
const redesignJa = {
|
|
71004
71797
|
"newSession.aiAuto": "AI 自動",
|
|
@@ -72146,7 +72939,7 @@ const personalChatsJa = {
|
|
|
72146
72939
|
"personalChats.creating": "チャットを開始しています…",
|
|
72147
72940
|
"personalChats.noOnlineMachine": "オンラインのマシンがありません。チャットを開始するにはマシンを起動してください。",
|
|
72148
72941
|
"personalChats.localMachinePreparing": "このコンピューターをSaycodeに接続しています。まもなく開始できます。",
|
|
72149
|
-
"personalChats.localMachineAsk": "
|
|
72942
|
+
"personalChats.localMachineAsk": "チャットを開始するには、このコンピューターを作業PCとして接続してください。",
|
|
72150
72943
|
"personalChats.localMachineFailed": "このコンピューターの自動接続に失敗しました。別の方法で接続できます。",
|
|
72151
72944
|
"personalChats.pickMachine": "このチャットを実行するマシンを選択してください",
|
|
72152
72945
|
"personalChats.createFailed": "チャットを開始できませんでした。",
|
|
@@ -73536,7 +74329,24 @@ const organizationTeamsJa = {
|
|
|
73536
74329
|
"organizationTeams.shareApply": "適用"
|
|
73537
74330
|
};
|
|
73538
74331
|
const gitAccountsJa = {
|
|
73539
|
-
"
|
|
74332
|
+
"projectStart.url": "URLからインポート",
|
|
74333
|
+
"projectStart.clearRepository": "リポジトリ選択を解除",
|
|
74334
|
+
"projectStart.providerPicker": "{provider} リポジトリを選択",
|
|
74335
|
+
"projectStart.manageAccess": "リポジトリアクセス管理",
|
|
74336
|
+
"projectStart.repositoryPlaceholder": "リポジトリを選択してください",
|
|
74337
|
+
"projectStart.localAccount": "このコンピューターのGitHubアカウント · @{account}",
|
|
74338
|
+
"projectStart.localGit": "このコンピューターのGit設定",
|
|
74339
|
+
"projectStart.machineGit": "作業PCのGit設定",
|
|
74340
|
+
"projectStart.githubAccount": "連携済みGitHubアカウント",
|
|
74341
|
+
"projectStart.gitlabAccount": "連携済みGitLabアカウント",
|
|
74342
|
+
"projectStart.organizationPat": "組織PAT · {name}",
|
|
74343
|
+
"projectStart.personalPat": "個人PAT",
|
|
74344
|
+
"projectStart.selectedConnectionHint": "選択したリポジトリの連携アカウントを使用します。",
|
|
74345
|
+
"projectStart.localAccountHint": "このコンピューターにログインしたGitHubアカウントでインポートします。",
|
|
74346
|
+
"projectStart.machineGitHint": "作業PCに設定されたGit認証を使用します。",
|
|
74347
|
+
"projectStart.accountHint": "選択した連携アカウントでリポジトリにアクセスします。",
|
|
74348
|
+
"projectStart.connectionUnavailable": "選択したリポジトリ接続を使用できません。接続状態を確認してください。",
|
|
74349
|
+
"settingsTab.gitAccounts": "リポジトリ",
|
|
73540
74350
|
"gitAccounts.title": "GitHub · GitLab アカウント",
|
|
73541
74351
|
"gitAccounts.description": "リポジトリのインポート・トリガー・PR 作業に使うアカウントを連携します。トークンはサーバーにのみ保管され、この端末には保存されません。",
|
|
73542
74352
|
"gitAccounts.refresh": "状態を更新",
|
|
@@ -73552,7 +74362,27 @@ const gitAccountsJa = {
|
|
|
73552
74362
|
"gitAccounts.connectFailed": "連携を開始できませんでした。しばらくしてから再試行してください。",
|
|
73553
74363
|
"gitAccounts.disconnectFailed": "連携を解除できませんでした。しばらくしてから再試行してください。",
|
|
73554
74364
|
"gitAccounts.warnings": "解除しましたが、確認が必要な項目があります:",
|
|
73555
|
-
"
|
|
74365
|
+
"projectStart.repositorySettings": "リポジトリ設定",
|
|
74366
|
+
"projectStart.allRepositories": "すべてのリポジトリ",
|
|
74367
|
+
"projectStart.selectedRepositories": "選択したリポジトリ",
|
|
74368
|
+
"projectStart.lastConnected": "最終接続",
|
|
74369
|
+
"projectStart.patTitle": "個人コードリポジトリ Personal Access Token",
|
|
74370
|
+
"projectStart.patIssue": "PAT発行ページを開く",
|
|
74371
|
+
"projectStart.patRegistered": "登録済み · 個人",
|
|
74372
|
+
"projectStart.patUnregistered": "未登録",
|
|
74373
|
+
"projectStart.patUpdatedAt": "最終更新",
|
|
74374
|
+
"projectStart.patConfirmDelete": "削除を確認",
|
|
74375
|
+
"projectStart.patDelete": "削除",
|
|
74376
|
+
"projectStart.patUpdate": "新しいPAT(更新)",
|
|
74377
|
+
"projectStart.patNew": "新しいPAT",
|
|
74378
|
+
"projectStart.patPlaceholder": "トークンを入力",
|
|
74379
|
+
"projectStart.patHide": "トークンを隠す",
|
|
74380
|
+
"projectStart.patShow": "トークンを表示",
|
|
74381
|
+
"projectStart.patSaveUpdate": "更新",
|
|
74382
|
+
"projectStart.patSaveNew": "登録",
|
|
74383
|
+
"projectStart.patSaved": "トークンを保存しました。",
|
|
74384
|
+
"projectStart.patDeleted": "トークンを削除しました。",
|
|
74385
|
+
"projectStart.loadingConnections": "リポジトリの接続情報を読み込み中…"
|
|
73556
74386
|
};
|
|
73557
74387
|
const agentBrowserConsoleJa = {
|
|
73558
74388
|
"browserPanel.agentBrowserConsole": "AIブラウザのタスクコンソール",
|
|
@@ -74123,6 +74953,51 @@ const designGuideJa = {
|
|
|
74123
74953
|
"designGuide.entry.body": "アプリの画面を作るときに使う色、文字、部品、チャット画面の基準を確認します。",
|
|
74124
74954
|
"designGuide.entry.open": "デザインガイドを開く"
|
|
74125
74955
|
};
|
|
74956
|
+
const prFollowThroughJa = {
|
|
74957
|
+
"prFollow.readyTitle": "PR #{number} はマージ可能です",
|
|
74958
|
+
"prFollow.readyBody": "CI・レビューボット・競合の確認をすべて通過しました。マージはご自身で行ってください。",
|
|
74959
|
+
"prFollow.mergeGuard.stacked": "このPRブランチにまだ反映されていないスタックPRがあります: {list}。今マージするとそれらのPRは孤立し、別途移す必要があります。",
|
|
74960
|
+
"prFollow.mergeGuard.botReviewing": "レビューボットがまだこのPRを確認中、または修正PRを作成中です。",
|
|
74961
|
+
"prFollow.mergeGuard.unknown": "残っているスタックPRがあるか確認できませんでした。",
|
|
74962
|
+
"prFollow.mergeGuard.confirm": "それでもマージしますか?",
|
|
74963
|
+
"prFollow.mergeGuard.title": "マージ前に確認してください",
|
|
74964
|
+
"prFollow.mergeGuard.mergeAnyway": "それでもマージ",
|
|
74965
|
+
"prFollow.enable": "PRウォッチ",
|
|
74966
|
+
"prFollow.enableTitle": "このPRのCI・レビューボット・競合を見守り、会話が待機中のときに修正すべきことを代わりに指示します。マージ可能になったらお知らせします。",
|
|
74967
|
+
"prFollow.disable": "PRウォッチを停止",
|
|
74968
|
+
"prFollow.dismiss": "PRウォッチの記録を消去",
|
|
74969
|
+
"prFollow.badge.prefix": "ウォッチ · {status}",
|
|
74970
|
+
"prFollow.badge.awaitingPr": "PR待ち",
|
|
74971
|
+
"prFollow.badge.watching": "見守り中",
|
|
74972
|
+
"prFollow.badge.checksPending": "チェック待ち",
|
|
74973
|
+
"prFollow.badge.botReviewing": "レビューボット待ち",
|
|
74974
|
+
"prFollow.badge.conflict": "競合",
|
|
74975
|
+
"prFollow.badge.checksFailed": "CI失敗",
|
|
74976
|
+
"prFollow.badge.stackedPr": "スタックPRあり",
|
|
74977
|
+
"prFollow.badge.botFindings": "ボット指摘の反映が必要",
|
|
74978
|
+
"prFollow.badge.ready": "マージ可能",
|
|
74979
|
+
"prFollow.badge.merged": "マージ済み",
|
|
74980
|
+
"prFollow.badge.lookupFailed": "PR状態を確認できません",
|
|
74981
|
+
"prFollow.badge.stopped.user": "オフ",
|
|
74982
|
+
"prFollow.badge.stopped.noPr": "PRが作成されませんでした",
|
|
74983
|
+
"prFollow.badge.stopped.closed": "PRがクローズされました",
|
|
74984
|
+
"prFollow.badge.stopped.budget": "自動指示の上限に到達",
|
|
74985
|
+
"prFollow.badge.stopped.repeatedFailure": "同じ失敗の繰り返し — 確認が必要",
|
|
74986
|
+
"prFollow.badge.stopped.sessionRemoved": "会話がありません",
|
|
74987
|
+
"prFollow.settings.saveFailed": "設定を保存できませんでした。",
|
|
74988
|
+
"prFollow.settings.followThrough": "オートパイロットのCommit & PR後にPRウォッチを開始",
|
|
74989
|
+
"prFollow.settings.followThroughHelp": "PRのCI失敗・レビューボットの修正PR・競合を1分ごとに確認し、会話が待機中のときに修正すべきことを代わりに指示します。マージ可能になると通知します。baseブランチへのマージは行いません。",
|
|
74990
|
+
"prFollow.settings.continueAfterMerge": "マージ後に残りの作業を続ける",
|
|
74991
|
+
"prFollow.settings.continueAfterMergeHelp": "PRがマージされると、同じ会話にspecの残りの作業を最新baseから新しいブランチで続けるよう指示します。残ったスタックPRがあればその処理も指示します。",
|
|
74992
|
+
"prFollow.settings.autoAnswer": "推奨案付きの質問にオートパイロットが代わりに答える",
|
|
74993
|
+
"prFollow.settings.autoAnswerHelp": "オートパイロット中にAIが推奨案を示して「進めますか?」で止まると「推奨案で進めて」と答えます(1回の実行で最大2回)。権限・費用・外部送信・元に戻しにくい変更は進めないよう併せて指示します。",
|
|
74994
|
+
"prFollow.settings.reviewer": "オートパイロットの独立レビューAI",
|
|
74995
|
+
"prFollow.settings.reviewerDefault": "デフォルト",
|
|
74996
|
+
"prFollow.settings.reviewerModel": "レビューモデル",
|
|
74997
|
+
"prFollow.settings.reviewerEffort": "レビューEffort",
|
|
74998
|
+
"prFollow.settings.reviewerHelp": "オートパイロットを独立レビューで有効にするときに使うAIです。有効にした時点の値がその実行に固定されます。",
|
|
74999
|
+
"prFollow.badge.title": "PRウォッチ · {status}"
|
|
75000
|
+
};
|
|
74126
75001
|
const catalogExtensionsJa = {
|
|
74127
75002
|
...chatSchedulesJa,
|
|
74128
75003
|
...mySettingsJa,
|
|
@@ -74203,9 +75078,20 @@ const catalogExtensionsJa = {
|
|
|
74203
75078
|
...lessonCandidateJa,
|
|
74204
75079
|
...gitAccountsJa,
|
|
74205
75080
|
...agentBrowserConsoleJa,
|
|
74206
|
-
...designGuideJa
|
|
75081
|
+
...designGuideJa,
|
|
75082
|
+
...prFollowThroughJa
|
|
74207
75083
|
};
|
|
74208
75084
|
const onboardingJa = {
|
|
75085
|
+
"folderBrowser.title": "フォルダーを選択",
|
|
75086
|
+
"folderBrowser.parent": "親フォルダー",
|
|
75087
|
+
"folderBrowser.folders": "フォルダー一覧",
|
|
75088
|
+
"folderBrowser.loading": "フォルダーを確認中…",
|
|
75089
|
+
"folderBrowser.empty": "サブフォルダーがありません。",
|
|
75090
|
+
"folderBrowser.newPath": "新しいパスから開始",
|
|
75091
|
+
"folderBrowser.choose": "このフォルダーを選択",
|
|
75092
|
+
"projectImport.drop": "zip・フォルダー・ファイルをドロップするかクリックして選択",
|
|
75093
|
+
"projectImport.excludeHint": "node_modules / .git / dist などの大きなフォルダーは自動的に除外されます",
|
|
75094
|
+
"projectImport.folder": "フォルダーを選択",
|
|
74209
75095
|
"onboarding.page.instructions": "Saycodeの指示",
|
|
74210
75096
|
"onboarding.instructions.body": "Saycodeの標準指示は、AIが作業を整理して結果を説明するのを助けます。使用するか選んでください。",
|
|
74211
75097
|
"onboarding.instructions.enable": "指示を使用する",
|
|
@@ -74405,7 +75291,8 @@ const onboardingJa = {
|
|
|
74405
75291
|
"onboarding.groups.error.capabilityRequired": "このマシンのSaycode CLIは組織の認証情報に対応していません。CLIを更新してからもう一度お試しください。",
|
|
74406
75292
|
"onboarding.groups.error.setupTokenRuntimeRequired": "このコンピューターに組織用setup-token runtimeがありません。Saycodeのcswap runtimeをインストールしてからもう一度お試しください。",
|
|
74407
75293
|
"onboarding.groups.error.happyDaemonUpdateRequired": "このコンピューターのSaycodeデーモンは組織の認証情報に対応していません。Desktopを更新してからもう一度お試しください。",
|
|
74408
|
-
"onboarding.groups.error.unknown": "組織の認証情報を接続できませんでした。しばらくしてからもう一度お試しください。"
|
|
75294
|
+
"onboarding.groups.error.unknown": "組織の認証情報を接続できませんでした。しばらくしてからもう一度お試しください。",
|
|
75295
|
+
"folderBrowser.path": "パス"
|
|
74409
75296
|
};
|
|
74410
75297
|
const standaloneRemoteOnboardingJa = {
|
|
74411
75298
|
"onboarding.optional": "(任意)",
|
|
@@ -74490,6 +75377,7 @@ const ja = {
|
|
|
74490
75377
|
"assetWorkflow.filesSelect": "選択したファイルを読む",
|
|
74491
75378
|
"assetWorkflow.draftsPrepare": "選択した会話の下書きと添付を準備",
|
|
74492
75379
|
...workJa,
|
|
75380
|
+
...workTemplateJa,
|
|
74493
75381
|
...FILE_ACCESS_TRANSLATIONS_JA,
|
|
74494
75382
|
...chatWorkJa,
|
|
74495
75383
|
...usageEntryJa,
|
|
@@ -76268,7 +77156,7 @@ const ja = {
|
|
|
76268
77156
|
"saycodePromptPreference.eyebrow": "エージェント指示を選択",
|
|
76269
77157
|
"saycodePromptPreference.title": "Saycode の既定指示を使用しますか?",
|
|
76270
77158
|
"saycodePromptPreference.body": "独自の Claude/Codex スキルやハーネスをそのまま使う場合はオフのままにしてください。後で設定から変更できます。",
|
|
76271
|
-
"saycodePromptPreference.defaultOnNotice": "子 Agent
|
|
77159
|
+
"saycodePromptPreference.defaultOnNotice": "子 Agent の呼び出し、内部作業の委任、成果物のインラインプレビュー、Saycode 使い方ガイドは既定でオンのままです。オフにするには、設定 → システムプロンプトで個別に無効にしてください。",
|
|
76272
77160
|
"saycodePromptPreference.keepOff": "オフのままにする",
|
|
76273
77161
|
"saycodePromptPreference.enable": "Saycode 指示をオンにする",
|
|
76274
77162
|
"saycodePromptPreference.saving": "保存中…",
|
|
@@ -76277,7 +77165,7 @@ const ja = {
|
|
|
76277
77165
|
"saycodePromptPreference.settingsEnable": "Saycode が提供する既定の行動指示を使用",
|
|
76278
77166
|
"saycodePromptPreference.settingsHelp": "ユーザーのシステムプロンプト、プロジェクト指示、インストール済みスキルとハーネスはこの設定に関係なく維持されます。",
|
|
76279
77167
|
"saycodePromptPreference.blocksTitle": "個別の指示",
|
|
76280
|
-
"saycodePromptPreference.blocksHelp": "子 Agent
|
|
77168
|
+
"saycodePromptPreference.blocksHelp": "子 Agent の呼び出し、内部作業の委任、成果物のインラインプレビュー、Saycode 使い方ガイドは既定でオンです。その他の未設定項目は上の設定に従います。",
|
|
76281
77169
|
"saycodePromptPreference.alwaysOnNotice": "チャットタイトルの生成は常に有効で、オフにできません。チャット一覧で会話を再び見つけるために必要です。",
|
|
76282
77170
|
"saycodePromptPreference.block.agentOrchestration.label": "子 Agent の呼び出し",
|
|
76283
77171
|
"saycodePromptPreference.block.agentOrchestration.description": "自然言語で依頼された作業を独立した子 Agent セッションに委任し、結果を回収するよう指示します。",
|
|
@@ -76289,6 +77177,8 @@ const ja = {
|
|
|
76289
77177
|
"saycodePromptPreference.block.coAuthoredCredit.description": "コミットメッセージに Claude と Happy の共同作成者表示を追加します。",
|
|
76290
77178
|
"saycodePromptPreference.block.inlinePreview.label": "成果物のインラインプレビュー",
|
|
76291
77179
|
"saycodePromptPreference.block.inlinePreview.description": "HTML の成果物を回答内にプレビュー表示するよう指示します。",
|
|
77180
|
+
"saycodePromptPreference.block.saycodeGuide.label": "Saycode 使い方ガイド",
|
|
77181
|
+
"saycodePromptPreference.block.saycodeGuide.description": "Saycode の使い方を尋ねると、関連するガイドとアプリの状況の一部(バージョン・利用形態・表示設定)をこの会話の AI に送ります。ガイド全体は会話の作業フォルダーの .aplus/saycode-guide に置き、リポジトリには含めません。質問かどうかの判定はこの端末内で行います。",
|
|
76292
77182
|
"saycodePromptPreference.loading": "アカウント設定を読み込み中…",
|
|
76293
77183
|
"standalone.zrokStatus": "zrok の状態",
|
|
76294
77184
|
"standalone.zrokNeeded": "zrok2 の設定が必要です",
|
|
@@ -80472,8 +81362,8 @@ function requireParser() {
|
|
|
80472
81362
|
};
|
|
80473
81363
|
Parser.prototype._parsePLTE = function(data) {
|
|
80474
81364
|
this._crc.write(data);
|
|
80475
|
-
let
|
|
80476
|
-
for (let i = 0; i <
|
|
81365
|
+
let entries2 = Math.floor(data.length / 3);
|
|
81366
|
+
for (let i = 0; i < entries2; i++) {
|
|
80477
81367
|
this._palette.push([data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 255]);
|
|
80478
81368
|
}
|
|
80479
81369
|
this.palette(this._palette);
|
|
@@ -83159,7 +84049,7 @@ async function runMachinesApprove(code, out) {
|
|
|
83159
84049
|
const outcome = await approvePairing({
|
|
83160
84050
|
serverUrl: credential.serverUrl,
|
|
83161
84051
|
ownerToken: credential.token,
|
|
83162
|
-
secret: decodeBase64$1(credential
|
|
84052
|
+
secret: decodeBase64$1(requireLegacySecret(credential)),
|
|
83163
84053
|
terminalPublicKey
|
|
83164
84054
|
}).catch((error) => {
|
|
83165
84055
|
throw new CliError(error instanceof Error ? error.message : String(error));
|
|
@@ -83345,8 +84235,8 @@ function mergeExtensionRevocations(known, incoming) {
|
|
|
83345
84235
|
}
|
|
83346
84236
|
return [...merged.values()].sort((left, right) => revocationKey(left).localeCompare(revocationKey(right)));
|
|
83347
84237
|
}
|
|
83348
|
-
function isExtensionRevoked(
|
|
83349
|
-
return
|
|
84238
|
+
function isExtensionRevoked(entries2, id2, version2) {
|
|
84239
|
+
return entries2.some((entry) => entry.id === id2 && entry.version === version2);
|
|
83350
84240
|
}
|
|
83351
84241
|
const DEFAULT_MAX_ARCHIVE_BYTES = 32 * 1024 * 1024;
|
|
83352
84242
|
const DEFAULT_MAX_CATALOG_BYTES = 1024 * 1024;
|
|
@@ -83985,7 +84875,7 @@ const ID_PATTERN$3 = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
|
|
|
83985
84875
|
function fail$7(path2, detail) {
|
|
83986
84876
|
throw new Error(`${path2}: ${detail}`);
|
|
83987
84877
|
}
|
|
83988
|
-
function record$
|
|
84878
|
+
function record$5(value2, path2) {
|
|
83989
84879
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) fail$7(path2, "expected object");
|
|
83990
84880
|
return value2;
|
|
83991
84881
|
}
|
|
@@ -84011,7 +84901,7 @@ function assertOnlyKeys(value2, allowed, path2) {
|
|
|
84011
84901
|
function contributionList(value2, path2, allowedKeys, parse2) {
|
|
84012
84902
|
return array$1(value2, path2).map((raw, index) => {
|
|
84013
84903
|
const entryPath = `${path2}[${index}]`;
|
|
84014
|
-
const entry = record$
|
|
84904
|
+
const entry = record$5(raw, entryPath);
|
|
84015
84905
|
assertOnlyKeys(entry, new Set(allowedKeys), entryPath);
|
|
84016
84906
|
return parse2(entry, entryPath);
|
|
84017
84907
|
});
|
|
@@ -84030,7 +84920,7 @@ function assertExtensionFileViewers(extensionId, viewers, panels, permissions) {
|
|
|
84030
84920
|
}
|
|
84031
84921
|
function parseMachineCommands(value2, extensionId, apiVersion) {
|
|
84032
84922
|
if (apiVersion < 3) fail$7("manifest.machineCommands", "requires Extension API version 3");
|
|
84033
|
-
const root = record$
|
|
84923
|
+
const root = record$5(value2, "manifest.machineCommands");
|
|
84034
84924
|
assertOnlyKeys(root, /* @__PURE__ */ new Set(["version", "profiles"]), "manifest.machineCommands");
|
|
84035
84925
|
if (root.version !== 1) fail$7("manifest.machineCommands.version", "unsupported version");
|
|
84036
84926
|
const profiles = contributionList(
|
|
@@ -84051,9 +84941,9 @@ function parseMachineCommands(value2, extensionId, apiVersion) {
|
|
|
84051
84941
|
});
|
|
84052
84942
|
if (!argv.length || argv.length > 32) fail$7(`${path2}.argv`, "expected 1-32 arguments");
|
|
84053
84943
|
const parameters = {};
|
|
84054
|
-
for (const [name, raw] of Object.entries(record$
|
|
84944
|
+
for (const [name, raw] of Object.entries(record$5(entry.parameters, `${path2}.parameters`))) {
|
|
84055
84945
|
if (!/^[a-z][a-zA-Z0-9_]{0,63}$/.test(name)) fail$7(`${path2}.parameters`, "invalid parameter name");
|
|
84056
|
-
const parameter = record$
|
|
84946
|
+
const parameter = record$5(raw, `${path2}.parameters.${name}`);
|
|
84057
84947
|
if (parameter.type === "string") {
|
|
84058
84948
|
assertOnlyKeys(parameter, /* @__PURE__ */ new Set(["type", "maxLength", "values"]), `${path2}.parameters.${name}`);
|
|
84059
84949
|
if (!Number.isInteger(parameter.maxLength) || Number(parameter.maxLength) < 1 || Number(parameter.maxLength) > 4096) fail$7(`${path2}.parameters.${name}.maxLength`, "invalid maxLength");
|
|
@@ -84249,7 +85139,7 @@ const MAX_EXTENSIONS = 16;
|
|
|
84249
85139
|
function fail$5(path2, detail) {
|
|
84250
85140
|
throw new Error(`${path2}: ${detail}`);
|
|
84251
85141
|
}
|
|
84252
|
-
function record$
|
|
85142
|
+
function record$4(value2, path2) {
|
|
84253
85143
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) fail$5(path2, "expected object");
|
|
84254
85144
|
return value2;
|
|
84255
85145
|
}
|
|
@@ -84276,12 +85166,12 @@ function uniqueList(values, path2) {
|
|
|
84276
85166
|
return values;
|
|
84277
85167
|
}
|
|
84278
85168
|
function parseLocalizations(value2, path2) {
|
|
84279
|
-
const localizations = record$
|
|
85169
|
+
const localizations = record$4(value2, path2);
|
|
84280
85170
|
if (Object.keys(localizations).length > MAX_LOCALES) fail$5(path2, "too many locales");
|
|
84281
85171
|
return Object.fromEntries(Object.entries(localizations).map(([locale, raw]) => {
|
|
84282
85172
|
if (!LOCALE_PATTERN$1.test(locale)) fail$5(path2, `invalid locale ${locale}`);
|
|
84283
85173
|
const localizationPath = `${path2}.${locale}`;
|
|
84284
|
-
const localization = record$
|
|
85174
|
+
const localization = record$4(raw, localizationPath);
|
|
84285
85175
|
exactKeys$2(localization, ["title"], [], localizationPath);
|
|
84286
85176
|
return [locale, { title: title(localization.title, `${localizationPath}.title`) }];
|
|
84287
85177
|
}));
|
|
@@ -84312,14 +85202,14 @@ function parseExtensionArtifactActions(value2, path2 = "manifest.contributes.art
|
|
|
84312
85202
|
if (!Array.isArray(value2) || value2.length > MAX_ACTIONS) fail$5(path2, "expected at most 32 actions");
|
|
84313
85203
|
return value2.map((raw, index) => {
|
|
84314
85204
|
const actionPath = `${path2}[${index}]`;
|
|
84315
|
-
const action = record$
|
|
85205
|
+
const action = record$4(raw, actionPath);
|
|
84316
85206
|
exactKeys$2(action, ["id", "title", "operation", "when"], ["localizations"], actionPath);
|
|
84317
85207
|
const parsedId = text$2(action.id, `${actionPath}.id`);
|
|
84318
85208
|
if (!ID_PATTERN$2.test(parsedId) || parsedId.includes("..")) fail$5(`${actionPath}.id`, "invalid stable identifier");
|
|
84319
85209
|
if (!EXTENSION_ARTIFACT_ACTION_OPERATIONS.includes(action.operation)) {
|
|
84320
85210
|
fail$5(`${actionPath}.operation`, "unsupported operation");
|
|
84321
85211
|
}
|
|
84322
|
-
const when = record$
|
|
85212
|
+
const when = record$4(action.when, `${actionPath}.when`);
|
|
84323
85213
|
exactKeys$2(when, ["sourceTypes", "extensions"], [], `${actionPath}.when`);
|
|
84324
85214
|
return {
|
|
84325
85215
|
id: parsedId,
|
|
@@ -84405,12 +85295,12 @@ function assertDepth(value2, path2, depth) {
|
|
|
84405
85295
|
function scalarParams(value2, path2) {
|
|
84406
85296
|
const source2 = plainRecord$1(value2, path2);
|
|
84407
85297
|
assertDepth(source2, path2, 1);
|
|
84408
|
-
const
|
|
84409
|
-
if (
|
|
84410
|
-
fail$4(`${path2} has ${
|
|
85298
|
+
const entries2 = Object.entries(source2);
|
|
85299
|
+
if (entries2.length > MAX_CHANNEL_PARAM_FIELDS) {
|
|
85300
|
+
fail$4(`${path2} has ${entries2.length} fields, over the ${MAX_CHANNEL_PARAM_FIELDS} field limit`);
|
|
84411
85301
|
}
|
|
84412
85302
|
const result2 = {};
|
|
84413
|
-
for (const [key, item] of
|
|
85303
|
+
for (const [key, item] of entries2) {
|
|
84414
85304
|
text$1(key, `${path2} field name`, 64);
|
|
84415
85305
|
if (typeof item === "string") result2[key] = text$1(item, `${path2}.${key}`);
|
|
84416
85306
|
else if (typeof item === "number") result2[key] = integer(item, `${path2}.${key}`, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
|
|
@@ -84836,7 +85726,7 @@ function parseChannelApiVersion(value2, apiVersion) {
|
|
|
84836
85726
|
return value2;
|
|
84837
85727
|
}
|
|
84838
85728
|
function parseContributions(value2, apiVersion, channelApiVersion) {
|
|
84839
|
-
const parsed2 = record$
|
|
85729
|
+
const parsed2 = record$5(value2, "manifest.contributes");
|
|
84840
85730
|
assertOnlyKeys(parsed2, CONTRIBUTION_KEYS, "manifest.contributes");
|
|
84841
85731
|
if (parsed2.channels !== void 0) {
|
|
84842
85732
|
if (apiVersion < 3) fail$7("manifest.contributes.channels", "requires Extension API version 3");
|
|
@@ -84908,12 +85798,12 @@ function parseContributions(value2, apiVersion, channelApiVersion) {
|
|
|
84908
85798
|
(entry, path2) => {
|
|
84909
85799
|
let localizations;
|
|
84910
85800
|
if (entry.localizations !== void 0) {
|
|
84911
|
-
const rawLocalizations = record$
|
|
85801
|
+
const rawLocalizations = record$5(entry.localizations, `${path2}.localizations`);
|
|
84912
85802
|
localizations = {};
|
|
84913
85803
|
for (const [locale, rawLocalization] of Object.entries(rawLocalizations)) {
|
|
84914
85804
|
if (!LOCALE_PATTERN.test(locale)) fail$7(`${path2}.localizations`, `invalid locale ${locale}`);
|
|
84915
85805
|
const localizationPath = `${path2}.localizations.${locale}`;
|
|
84916
|
-
const localization = record$
|
|
85806
|
+
const localization = record$5(rawLocalization, localizationPath);
|
|
84917
85807
|
assertOnlyKeys(
|
|
84918
85808
|
localization,
|
|
84919
85809
|
/* @__PURE__ */ new Set(["title", "description", "firstPrompt"]),
|
|
@@ -84945,7 +85835,7 @@ function parseContributions(value2, apiVersion, channelApiVersion) {
|
|
|
84945
85835
|
"manifest.contributes.machineActions",
|
|
84946
85836
|
["id", "title", "command", "when"],
|
|
84947
85837
|
(entry, path2) => {
|
|
84948
|
-
const when = record$
|
|
85838
|
+
const when = record$5(entry.when, `${path2}.when`);
|
|
84949
85839
|
assertOnlyKeys(when, /* @__PURE__ */ new Set(["online"]), `${path2}.when`);
|
|
84950
85840
|
if (when.online !== true) fail$7(`${path2}.when.online`, "expected true");
|
|
84951
85841
|
return {
|
|
@@ -84978,14 +85868,14 @@ function parseContributions(value2, apiVersion, channelApiVersion) {
|
|
|
84978
85868
|
return result2;
|
|
84979
85869
|
}
|
|
84980
85870
|
function parseExtensionManifest(value2, options) {
|
|
84981
|
-
const manifest2 = record$
|
|
85871
|
+
const manifest2 = record$5(value2, "manifest");
|
|
84982
85872
|
assertOnlyKeys(manifest2, TOP_LEVEL_KEYS, "manifest");
|
|
84983
85873
|
const parsedId = id$1(manifest2.id, "manifest.id");
|
|
84984
85874
|
const version2 = string(manifest2.version, "manifest.version");
|
|
84985
85875
|
if (!SEMVER_PATTERN$1.test(version2)) fail$7("manifest.version", "expected semantic version");
|
|
84986
85876
|
const apiVersion = parseApiVersion(manifest2.apiVersion, options);
|
|
84987
85877
|
const channelApiVersion = parseChannelApiVersion(manifest2.channelApiVersion, apiVersion);
|
|
84988
|
-
const engines = record$
|
|
85878
|
+
const engines = record$5(manifest2.engines, "manifest.engines");
|
|
84989
85879
|
assertOnlyKeys(engines, /* @__PURE__ */ new Set(["saycode"]), "manifest.engines");
|
|
84990
85880
|
const saycodeEngine = string(engines.saycode, "manifest.engines.saycode");
|
|
84991
85881
|
if (!ENGINE_RANGE_PATTERN.test(saycodeEngine)) {
|
|
@@ -85798,7 +86688,7 @@ function syncSocketTokens(auth) {
|
|
|
85798
86688
|
}
|
|
85799
86689
|
function createChannelAdapterCandidateSource(deps) {
|
|
85800
86690
|
const manifests = /* @__PURE__ */ new Map();
|
|
85801
|
-
const
|
|
86691
|
+
const entries2 = () => {
|
|
85802
86692
|
try {
|
|
85803
86693
|
return deps.entries();
|
|
85804
86694
|
} catch {
|
|
@@ -85806,7 +86696,7 @@ function createChannelAdapterCandidateSource(deps) {
|
|
|
85806
86696
|
}
|
|
85807
86697
|
};
|
|
85808
86698
|
return {
|
|
85809
|
-
candidates: () => Object.freeze(
|
|
86699
|
+
candidates: () => Object.freeze(entries2().flatMap(({ id: id2, state }) => {
|
|
85810
86700
|
const manifest2 = manifests.get(`${id2}@${state.installedVersion}`);
|
|
85811
86701
|
if (!manifest2) return [];
|
|
85812
86702
|
return [{
|
|
@@ -85817,7 +86707,7 @@ function createChannelAdapterCandidateSource(deps) {
|
|
|
85817
86707
|
}];
|
|
85818
86708
|
})),
|
|
85819
86709
|
refresh: async () => {
|
|
85820
|
-
for (const { id: id2, state } of
|
|
86710
|
+
for (const { id: id2, state } of entries2()) {
|
|
85821
86711
|
const key = `${id2}@${state.installedVersion}`;
|
|
85822
86712
|
if (manifests.has(key)) continue;
|
|
85823
86713
|
try {
|
|
@@ -89955,6 +90845,37 @@ const SLACK_FILE_UPLOAD_OPERATIONS = Object.freeze({
|
|
|
89955
90845
|
allowedParams: Object.freeze(["files", "channel_id", "thread_ts"])
|
|
89956
90846
|
})
|
|
89957
90847
|
});
|
|
90848
|
+
const SLACK_REACTION_NAMES = Object.freeze({
|
|
90849
|
+
"👀": "eyes",
|
|
90850
|
+
"✍": "writing_hand",
|
|
90851
|
+
"🤔": "thinking_face",
|
|
90852
|
+
"👍": "+1",
|
|
90853
|
+
"💔": "broken_heart",
|
|
90854
|
+
"🙈": "see_no_evil"
|
|
90855
|
+
});
|
|
90856
|
+
function slackReactionName(glyph) {
|
|
90857
|
+
return Object.prototype.hasOwnProperty.call(SLACK_REACTION_NAMES, glyph) ? SLACK_REACTION_NAMES[glyph] : null;
|
|
90858
|
+
}
|
|
90859
|
+
const reactionOperation = (pathTemplate) => Object.freeze({
|
|
90860
|
+
method: "POST",
|
|
90861
|
+
direction: "outbound",
|
|
90862
|
+
pathTemplate,
|
|
90863
|
+
credentialPlacement: "authorizationBearer",
|
|
90864
|
+
parameterPlacement: "jsonBody",
|
|
90865
|
+
allowedParams: Object.freeze(["channel", "timestamp", "name"])
|
|
90866
|
+
});
|
|
90867
|
+
const SLACK_REACTION_OPERATIONS = Object.freeze({
|
|
90868
|
+
reactionsAdd: reactionOperation("/api/reactions.add"),
|
|
90869
|
+
reactionsRemove: reactionOperation("/api/reactions.remove")
|
|
90870
|
+
});
|
|
90871
|
+
const SLACK_REACTION_MAPPING = Object.freeze({
|
|
90872
|
+
operation: "reactionsAdd",
|
|
90873
|
+
removeOperation: "reactionsRemove",
|
|
90874
|
+
conversationParam: "channel",
|
|
90875
|
+
messageIdParam: "timestamp",
|
|
90876
|
+
reactionParam: "name",
|
|
90877
|
+
value: "emoji-name"
|
|
90878
|
+
});
|
|
89958
90879
|
const MAX_SELECTOR_DEPTH = 6;
|
|
89959
90880
|
const MAX_EVENTS_PER_PAYLOAD = 100;
|
|
89960
90881
|
const TELEGRAM = Object.freeze({
|
|
@@ -90055,7 +90976,8 @@ const TELEGRAM = Object.freeze({
|
|
|
90055
90976
|
operation: "setMessageReaction",
|
|
90056
90977
|
conversationParam: "chat_id",
|
|
90057
90978
|
messageIdParam: "message_id",
|
|
90058
|
-
reactionParam: "reaction"
|
|
90979
|
+
reactionParam: "reaction",
|
|
90980
|
+
value: "reaction-list"
|
|
90059
90981
|
})
|
|
90060
90982
|
});
|
|
90061
90983
|
const SLACK$1 = Object.freeze({
|
|
@@ -90128,7 +91050,8 @@ const SLACK$1 = Object.freeze({
|
|
|
90128
91050
|
allowedParams: Object.freeze(["channel", "ts", "text"])
|
|
90129
91051
|
},
|
|
90130
91052
|
// Files attached to a final answer (specs/channel-file-replies). Core-only, like the two above.
|
|
90131
|
-
...SLACK_FILE_UPLOAD_OPERATIONS
|
|
91053
|
+
...SLACK_FILE_UPLOAD_OPERATIONS,
|
|
91054
|
+
...SLACK_REACTION_OPERATIONS
|
|
90132
91055
|
}),
|
|
90133
91056
|
inbound: Object.freeze({
|
|
90134
91057
|
eventsPath: Object.freeze(["events"]),
|
|
@@ -90153,8 +91076,8 @@ const SLACK$1 = Object.freeze({
|
|
|
90153
91076
|
conversationParam: "channel",
|
|
90154
91077
|
messageIdParam: "ts"
|
|
90155
91078
|
}),
|
|
90156
|
-
//
|
|
90157
|
-
reaction:
|
|
91079
|
+
// The stage as a reaction on the sender's message (specs/messenger-progress-stages R15).
|
|
91080
|
+
reaction: SLACK_REACTION_MAPPING
|
|
90158
91081
|
});
|
|
90159
91082
|
const DISCORD$1 = Object.freeze({
|
|
90160
91083
|
provider: "discord",
|
|
@@ -90372,7 +91295,7 @@ class ChannelApprovalPayloadError extends Error {
|
|
|
90372
91295
|
function fail$1(detail) {
|
|
90373
91296
|
throw new ChannelApprovalPayloadError(detail);
|
|
90374
91297
|
}
|
|
90375
|
-
function record$
|
|
91298
|
+
function record$3(value2, detail) {
|
|
90376
91299
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) fail$1(detail);
|
|
90377
91300
|
return value2;
|
|
90378
91301
|
}
|
|
@@ -90409,14 +91332,14 @@ const DISCORD_STRUCTURE = ["type", "components", "style", "label", "custom_id"];
|
|
|
90409
91332
|
function verifySlackApprovalBlocks(blocks, presentation) {
|
|
90410
91333
|
const list2 = array(blocks, "blocks must be an array");
|
|
90411
91334
|
if (list2.length !== 2) fail$1("blocks must be exactly a section and an actions row");
|
|
90412
|
-
const section = record$
|
|
91335
|
+
const section = record$3(list2[0], "the first block must be an object");
|
|
90413
91336
|
exact(section, ["type", "text"], "the section block carries unexpected fields");
|
|
90414
91337
|
if (section.type !== "section") fail$1("the first block must be a section");
|
|
90415
|
-
const sectionText = record$
|
|
91338
|
+
const sectionText = record$3(section.text, "the section text must be an object");
|
|
90416
91339
|
exact(sectionText, ["type", "text"], "the section text carries unexpected fields");
|
|
90417
91340
|
if (sectionText.type !== "plain_text") fail$1("the section text must be plain text");
|
|
90418
91341
|
if (sectionText.text !== presentation.text) fail$1("the section text is not the approved reply");
|
|
90419
|
-
const actions = record$
|
|
91342
|
+
const actions = record$3(list2[1], "the second block must be an object");
|
|
90420
91343
|
exact(actions, ["type", "elements"], "the actions block carries unexpected fields");
|
|
90421
91344
|
if (actions.type !== "actions") fail$1("the second block must be an actions row");
|
|
90422
91345
|
const elements = array(actions.elements, "elements must be an array");
|
|
@@ -90426,11 +91349,11 @@ function verifySlackApprovalBlocks(blocks, presentation) {
|
|
|
90426
91349
|
{ actionId: presentation.actionIds.deny, label: presentation.denyLabel }
|
|
90427
91350
|
];
|
|
90428
91351
|
for (const [index, element] of elements.entries()) {
|
|
90429
|
-
const button = record$
|
|
91352
|
+
const button = record$3(element, "each element must be an object");
|
|
90430
91353
|
exact(button, ["type", "action_id", "text"], "a button carries unexpected fields");
|
|
90431
91354
|
if (button.type !== "button") fail$1("every element must be a button");
|
|
90432
91355
|
if (button.action_id !== expected[index].actionId) fail$1("a button names an unexpected action");
|
|
90433
|
-
const label = record$
|
|
91356
|
+
const label = record$3(button.text, "a button label must be an object");
|
|
90434
91357
|
exact(label, ["type", "text"], "a button label carries unexpected fields");
|
|
90435
91358
|
if (label.type !== "plain_text") fail$1("a button label must be plain text");
|
|
90436
91359
|
if (label.text !== expected[index].label) fail$1("a button label is not the one Core supplied");
|
|
@@ -90441,7 +91364,7 @@ function verifySlackApprovalBlocks(blocks, presentation) {
|
|
|
90441
91364
|
function verifyDiscordApprovalComponents(components, presentation) {
|
|
90442
91365
|
const list2 = array(components, "components must be an array");
|
|
90443
91366
|
if (list2.length !== 1) fail$1("exactly one action row is permitted");
|
|
90444
|
-
const row2 = record$
|
|
91367
|
+
const row2 = record$3(list2[0], "the action row must be an object");
|
|
90445
91368
|
exact(row2, ["type", "components"], "the action row carries unexpected fields");
|
|
90446
91369
|
if (row2.type !== 1) fail$1("the first component must be an action row");
|
|
90447
91370
|
const buttons = array(row2.components, "row components must be an array");
|
|
@@ -90451,7 +91374,7 @@ function verifyDiscordApprovalComponents(components, presentation) {
|
|
|
90451
91374
|
{ actionId: presentation.actionIds.deny, label: presentation.denyLabel, style: DISCORD_DANGER }
|
|
90452
91375
|
];
|
|
90453
91376
|
for (const [index, element] of buttons.entries()) {
|
|
90454
|
-
const button = record$
|
|
91377
|
+
const button = record$3(element, "each component must be an object");
|
|
90455
91378
|
exact(button, ["type", "style", "label", "custom_id"], "a button carries unexpected fields");
|
|
90456
91379
|
if (button.type !== 2) fail$1("every component must be a button");
|
|
90457
91380
|
if (button.style !== expected[index].style) fail$1("a button uses an unsupported style");
|
|
@@ -90728,11 +91651,11 @@ function mapEntry(entry, contract, context, command, action) {
|
|
|
90728
91651
|
};
|
|
90729
91652
|
}
|
|
90730
91653
|
function mapChannelInboundPayload(contract, payload, context) {
|
|
90731
|
-
const
|
|
90732
|
-
if (!Array.isArray(
|
|
91654
|
+
const entries2 = readPath$1(payload, contract.inbound.eventsPath);
|
|
91655
|
+
if (!Array.isArray(entries2)) return { events: [], dropped: [] };
|
|
90733
91656
|
const events = [];
|
|
90734
91657
|
const dropped = [];
|
|
90735
|
-
for (const [index, entry] of
|
|
91658
|
+
for (const [index, entry] of entries2.slice(0, MAX_EVENTS_PER_PAYLOAD).entries()) {
|
|
90736
91659
|
const normalized = normalizeChannelInboundEntry(contract.provider, entry);
|
|
90737
91660
|
if (!normalized) {
|
|
90738
91661
|
dropped.push({ index, reason: "not a command this connection can execute" });
|
|
@@ -90747,7 +91670,7 @@ function mapChannelInboundPayload(contract, payload, context) {
|
|
|
90747
91670
|
verifiedBotRef: context.verifiedBotRef ?? null
|
|
90748
91671
|
})));
|
|
90749
91672
|
}
|
|
90750
|
-
if (
|
|
91673
|
+
if (entries2.length > MAX_EVENTS_PER_PAYLOAD) {
|
|
90751
91674
|
dropped.push({ index: MAX_EVENTS_PER_PAYLOAD, reason: "payload exceeded the per-poll event limit" });
|
|
90752
91675
|
}
|
|
90753
91676
|
return { events: Object.freeze(events), dropped: Object.freeze(dropped) };
|
|
@@ -90945,12 +91868,12 @@ function boundedStructure(value2, key, depth) {
|
|
|
90945
91868
|
if (Object.getPrototypeOf(source2) !== Object.prototype && Object.getPrototypeOf(source2) !== null) {
|
|
90946
91869
|
reject("PARAM_NOT_STRUCTURED", `param ${key} must be a plain object`);
|
|
90947
91870
|
}
|
|
90948
|
-
const
|
|
90949
|
-
if (
|
|
91871
|
+
const entries2 = Object.entries(source2);
|
|
91872
|
+
if (entries2.length > MAX_STRUCTURED_PARAM_ENTRIES) {
|
|
90950
91873
|
reject("STRUCTURED_PARAM_TOO_WIDE", `param ${key} has more than ${MAX_STRUCTURED_PARAM_ENTRIES} fields`);
|
|
90951
91874
|
}
|
|
90952
91875
|
const result2 = {};
|
|
90953
|
-
for (const [field, item] of
|
|
91876
|
+
for (const [field, item] of entries2) {
|
|
90954
91877
|
if (field === "__proto__" || field === "constructor" || field === "prototype") {
|
|
90955
91878
|
reject("PARAM_NOT_STRUCTURED", `param ${key} contains a reserved field name`);
|
|
90956
91879
|
}
|
|
@@ -91487,20 +92410,29 @@ async function reactChannelProgress(input) {
|
|
|
91487
92410
|
const contract = channelProviderContract(input.provider);
|
|
91488
92411
|
const mapping = contract.reaction;
|
|
91489
92412
|
if (!mapping || !input.emoji) return { ok: false, retryable: false };
|
|
92413
|
+
const value2 = (glyph) => mapping.value === "emoji-name" ? slackReactionName(glyph) : [{ type: "emoji", emoji: glyph }];
|
|
92414
|
+
const reaction = value2(input.emoji);
|
|
92415
|
+
if (reaction === null) return { ok: false, retryable: false };
|
|
92416
|
+
const send = (operation, reactionValue) => input.executor.execute(input.connectionId, {
|
|
92417
|
+
operation,
|
|
92418
|
+
params: {
|
|
92419
|
+
[mapping.conversationParam]: input.destination.conversationRef,
|
|
92420
|
+
[mapping.messageIdParam]: input.messageId,
|
|
92421
|
+
[mapping.reactionParam]: reactionValue
|
|
92422
|
+
},
|
|
92423
|
+
credentialRevision: input.connectionRevision
|
|
92424
|
+
});
|
|
92425
|
+
const previous = input.previousEmoji ? value2(input.previousEmoji) : null;
|
|
92426
|
+
if (mapping.removeOperation && previous !== null && input.previousEmoji !== input.emoji) {
|
|
92427
|
+
await send(mapping.removeOperation, previous).catch(() => void 0);
|
|
92428
|
+
}
|
|
91490
92429
|
let result2;
|
|
91491
92430
|
try {
|
|
91492
|
-
result2 = await
|
|
91493
|
-
operation: mapping.operation,
|
|
91494
|
-
params: {
|
|
91495
|
-
[mapping.conversationParam]: input.destination.conversationRef,
|
|
91496
|
-
[mapping.messageIdParam]: input.messageId,
|
|
91497
|
-
[mapping.reactionParam]: [{ type: "emoji", emoji: input.emoji }]
|
|
91498
|
-
},
|
|
91499
|
-
credentialRevision: input.connectionRevision
|
|
91500
|
-
});
|
|
92431
|
+
result2 = await send(mapping.operation, reaction);
|
|
91501
92432
|
} catch {
|
|
91502
92433
|
return { ok: false, retryable: true };
|
|
91503
92434
|
}
|
|
92435
|
+
if (result2.body?.error === "already_reacted") return { ok: true };
|
|
91504
92436
|
return classifyProgressResult(contract, result2, input.onDiagnostic);
|
|
91505
92437
|
}
|
|
91506
92438
|
async function editChannelProgress(input) {
|
|
@@ -91840,9 +92772,9 @@ async function withdrawIntakeDurably(cancelled, connectionKeys, deps) {
|
|
|
91840
92772
|
const EMPTY_WITHDRAWAL = Object.freeze({
|
|
91841
92773
|
unsafeToReopen: Object.freeze([])
|
|
91842
92774
|
});
|
|
91843
|
-
async function journalCancelledIntake(
|
|
92775
|
+
async function journalCancelledIntake(entries2, deps) {
|
|
91844
92776
|
const undurable = [];
|
|
91845
|
-
for (const entry of
|
|
92777
|
+
for (const entry of entries2) {
|
|
91846
92778
|
try {
|
|
91847
92779
|
await deps.journal.record(entry);
|
|
91848
92780
|
} catch {
|
|
@@ -92036,6 +92968,7 @@ class ChannelProgressRails {
|
|
|
92036
92968
|
}
|
|
92037
92969
|
/** requestId → its rail and the session it belongs to. Both are part of every match. */
|
|
92038
92970
|
rails = /* @__PURE__ */ new Map();
|
|
92971
|
+
receipts = /* @__PURE__ */ new Map();
|
|
92039
92972
|
get size() {
|
|
92040
92973
|
return this.rails.size;
|
|
92041
92974
|
}
|
|
@@ -92059,6 +92992,9 @@ class ChannelProgressRails {
|
|
|
92059
92992
|
const reactTo = source2?.destination.replyToTransportEventId ?? null;
|
|
92060
92993
|
const reacting = react !== void 0 && source2 !== void 0 && reactTo !== null;
|
|
92061
92994
|
let target = null;
|
|
92995
|
+
const receipt = reacting ? this.receipts.get(requestId) : void 0;
|
|
92996
|
+
if (reacting) this.receipts.delete(requestId);
|
|
92997
|
+
let shown = null;
|
|
92062
92998
|
const rail = new ChannelProgressRail({
|
|
92063
92999
|
requestId,
|
|
92064
93000
|
// From acceptance, which is what the tracker's clock means and what the reader has been
|
|
@@ -92078,7 +93014,10 @@ class ChannelProgressRails {
|
|
|
92078
93014
|
connectionRevision: target.generation,
|
|
92079
93015
|
destination: target.destination
|
|
92080
93016
|
};
|
|
92081
|
-
|
|
93017
|
+
if (!reacting) return this.options.edit(input, id2, text2);
|
|
93018
|
+
const result2 = await react(input, id2, text2, shown);
|
|
93019
|
+
if (result2.ok) shown = text2;
|
|
93020
|
+
return result2;
|
|
92082
93021
|
}
|
|
92083
93022
|
},
|
|
92084
93023
|
...this.options.onDiagnostic ? { onDiagnostic: this.options.onDiagnostic } : {}
|
|
@@ -92094,10 +93033,74 @@ class ChannelProgressRails {
|
|
|
92094
93033
|
});
|
|
92095
93034
|
if (reacting) {
|
|
92096
93035
|
target = { generation: source2.connectionRevision, destination: source2.destination };
|
|
92097
|
-
|
|
93036
|
+
if (receipt) void receipt.drawn.then(() => {
|
|
93037
|
+
shown = receipt.shown;
|
|
93038
|
+
rail.adopt(reactTo, shown ?? "");
|
|
93039
|
+
});
|
|
93040
|
+
else rail.adopt(reactTo, "");
|
|
92098
93041
|
}
|
|
92099
93042
|
return reacting;
|
|
92100
93043
|
}
|
|
93044
|
+
/**
|
|
93045
|
+
* Draws 👀 on the sender's message the moment it is admitted (R16), before the adapter's receive
|
|
93046
|
+
* loop and the dispatch. Display only and never awaited by the caller; `expect` takes it over,
|
|
93047
|
+
* `settleReceipt` closes it when no turn follows.
|
|
93048
|
+
*/
|
|
93049
|
+
acknowledgeReceipt(requestId, source2) {
|
|
93050
|
+
const react = this.options.react;
|
|
93051
|
+
const messageId2 = source2.destination.replyToTransportEventId;
|
|
93052
|
+
if (!react || !messageId2 || !this.options.isActive() || !isCoreRequestId(requestId)) return;
|
|
93053
|
+
if (this.rails.has(requestId) || this.receipts.has(requestId)) return;
|
|
93054
|
+
if (this.receipts.size >= MAX_PROGRESS_RAILS) return;
|
|
93055
|
+
const receipt = {
|
|
93056
|
+
generation: source2.connectionRevision,
|
|
93057
|
+
destination: source2.destination,
|
|
93058
|
+
messageId: messageId2,
|
|
93059
|
+
shown: null,
|
|
93060
|
+
drawn: Promise.resolve({ ok: true }),
|
|
93061
|
+
settling: false,
|
|
93062
|
+
cancelTimer: null
|
|
93063
|
+
};
|
|
93064
|
+
this.receipts.set(requestId, receipt);
|
|
93065
|
+
receipt.drawn = this.drawReceipt(receipt, CHANNEL_STAGE_REACTION.accepted);
|
|
93066
|
+
}
|
|
93067
|
+
/** No rail followed this request — a command, a refusal, a question back: close its 👀 (R16). */
|
|
93068
|
+
settleReceipt(requestId, outcome) {
|
|
93069
|
+
const receipt = this.receipts.get(requestId);
|
|
93070
|
+
if (!receipt || receipt.settling) return;
|
|
93071
|
+
receipt.settling = true;
|
|
93072
|
+
const finish = async (attempt2) => {
|
|
93073
|
+
if (this.receipts.get(requestId) !== receipt) return;
|
|
93074
|
+
const result2 = await this.drawReceipt(receipt, CHANNEL_STAGE_REACTION[outcome]);
|
|
93075
|
+
if (this.receipts.get(requestId) !== receipt) return;
|
|
93076
|
+
if (result2.ok || !result2.retryable || attempt2 >= PROGRESS_TERMINAL_ATTEMPTS) {
|
|
93077
|
+
this.receipts.delete(requestId);
|
|
93078
|
+
return;
|
|
93079
|
+
}
|
|
93080
|
+
receipt.cancelTimer = this.options.setTimer(() => {
|
|
93081
|
+
receipt.cancelTimer = null;
|
|
93082
|
+
void finish(attempt2 + 1);
|
|
93083
|
+
}, PROGRESS_MIN_EDIT_INTERVAL_MS);
|
|
93084
|
+
};
|
|
93085
|
+
void receipt.drawn.then(() => finish(1));
|
|
93086
|
+
}
|
|
93087
|
+
async drawReceipt(receipt, emoji2) {
|
|
93088
|
+
if (!this.options.react || !this.options.isActive() || this.options.currentGeneration() !== receipt.generation) {
|
|
93089
|
+
return { ok: false, retryable: false };
|
|
93090
|
+
}
|
|
93091
|
+
try {
|
|
93092
|
+
const result2 = await this.options.react({
|
|
93093
|
+
provider: this.options.provider,
|
|
93094
|
+
connectionId: this.options.connectionId,
|
|
93095
|
+
connectionRevision: receipt.generation,
|
|
93096
|
+
destination: receipt.destination
|
|
93097
|
+
}, receipt.messageId, emoji2, receipt.shown);
|
|
93098
|
+
if (result2.ok) receipt.shown = emoji2;
|
|
93099
|
+
return result2;
|
|
93100
|
+
} catch {
|
|
93101
|
+
return { ok: false, retryable: true };
|
|
93102
|
+
}
|
|
93103
|
+
}
|
|
92101
93104
|
/**
|
|
92102
93105
|
* Points this request's rail at the acknowledgement the outbox just delivered.
|
|
92103
93106
|
*
|
|
@@ -92151,6 +93154,8 @@ class ChannelProgressRails {
|
|
|
92151
93154
|
closeAll() {
|
|
92152
93155
|
for (const entry of this.rails.values()) entry.rail.dispose();
|
|
92153
93156
|
this.rails.clear();
|
|
93157
|
+
for (const receipt of this.receipts.values()) receipt.cancelTimer?.();
|
|
93158
|
+
this.receipts.clear();
|
|
92154
93159
|
}
|
|
92155
93160
|
}
|
|
92156
93161
|
const MAX_FINAL_ANSWER_BYTES = 64 * 1024;
|
|
@@ -93058,12 +94063,13 @@ class ChannelConnectionComposition {
|
|
|
93058
94063
|
executor: this.outboundExecutor,
|
|
93059
94064
|
...this.options.onDiagnostic ? { onDiagnostic: this.options.onDiagnostic } : {}
|
|
93060
94065
|
}),
|
|
93061
|
-
// Only where the provider can react (Telegram): the others keep the bubble
|
|
94066
|
+
// Only where the provider can react (Telegram R13, Slack R15): the others keep the bubble.
|
|
93062
94067
|
...channelProviderContract(input.provider).reaction ? {
|
|
93063
|
-
react: (target, messageId2, emoji2) => reactChannelProgress({
|
|
94068
|
+
react: (target, messageId2, emoji2, shown) => reactChannelProgress({
|
|
93064
94069
|
...target,
|
|
93065
94070
|
messageId: messageId2,
|
|
93066
94071
|
emoji: emoji2,
|
|
94072
|
+
previousEmoji: shown,
|
|
93067
94073
|
executor: this.outboundExecutor,
|
|
93068
94074
|
...this.options.onDiagnostic ? { onDiagnostic: this.options.onDiagnostic } : {}
|
|
93069
94075
|
})
|
|
@@ -93331,6 +94337,7 @@ async function dispatchChannelControl(input, deps) {
|
|
|
93331
94337
|
boundAt: deps.now()
|
|
93332
94338
|
}, word.kind === "help" ? channelHelpText() : word.kind === "welcome" ? channelWelcomeText() : channelUnknownCommandText(word.word));
|
|
93333
94339
|
}
|
|
94340
|
+
parts2.progress.settleReceipt(input.requestId, "completed");
|
|
93334
94341
|
parts2.reply.unbind(input.requestId);
|
|
93335
94342
|
await settleIntakeEntry(parts2.queue, input.requestId, "executed", deps.settlement);
|
|
93336
94343
|
deps.onSettled?.(input.requestId);
|
|
@@ -93378,10 +94385,11 @@ async function dispatchChannelControl(input, deps) {
|
|
|
93378
94385
|
deps.onSettled?.(input.requestId);
|
|
93379
94386
|
}
|
|
93380
94387
|
const startedWork = result2.ok && result2.sessionId !== null;
|
|
94388
|
+
let reacting = false;
|
|
93381
94389
|
if (answerable()) {
|
|
93382
94390
|
const sessionId = (result2.ok ? result2.sessionId : null) ?? `command:${input.connectionId}`;
|
|
93383
94391
|
const acknowledged = result2.ok && result2.text === CHANNEL_ACK_TEXT();
|
|
93384
|
-
|
|
94392
|
+
reacting = startedWork && bound !== null && !SYNCHRONOUS_OPERATIONS.has(bound.operation) && parts2.progress.expect(input.requestId, sessionId, acknowledged ? {
|
|
93385
94393
|
destination: input.event.destination,
|
|
93386
94394
|
connectionRevision: input.event.connectionRevision
|
|
93387
94395
|
} : void 0);
|
|
@@ -93394,7 +94402,9 @@ async function dispatchChannelControl(input, deps) {
|
|
|
93394
94402
|
boundAt: deps.now()
|
|
93395
94403
|
}, result2.ok ? result2.text : ambiguous ? CHANNEL_DISPATCH_UNCONFIRMED() : REFUSAL_TEXT());
|
|
93396
94404
|
}
|
|
93397
|
-
|
|
94405
|
+
const terminal2 = !result2.ok || bound === null || !startedWork || SYNCHRONOUS_OPERATIONS.has(bound.operation);
|
|
94406
|
+
if (!reacting && !ambiguous && terminal2) parts2.progress.settleReceipt(input.requestId, result2.ok ? "completed" : "failed");
|
|
94407
|
+
if (terminal2) {
|
|
93398
94408
|
const state = result2.ok ? "executed" : ambiguous ? "unknown" : "failed";
|
|
93399
94409
|
await settleIntakeEntry(parts2.queue, input.requestId, state, deps.settlement);
|
|
93400
94410
|
if (result2.ok) {
|
|
@@ -93659,8 +94669,8 @@ class ChannelThreadOwnership {
|
|
|
93659
94669
|
* Dropping the rest is what keeps a revoke-and-repair from handing an old thread to the new
|
|
93660
94670
|
* pairing — and a restart from handing a claimed thread to whoever writes in first.
|
|
93661
94671
|
*/
|
|
93662
|
-
restore(
|
|
93663
|
-
for (const entry of
|
|
94672
|
+
restore(entries2, currentGeneration) {
|
|
94673
|
+
for (const entry of entries2) {
|
|
93664
94674
|
if (entry.connectionRevision !== currentGeneration) continue;
|
|
93665
94675
|
const id2 = keyOf$1(entry);
|
|
93666
94676
|
if (!this.owners.has(id2)) {
|
|
@@ -93726,8 +94736,8 @@ class ChannelThreadSelection {
|
|
|
93726
94736
|
* Entries stamped with any other revision are dropped rather than restamped: a thread that was
|
|
93727
94737
|
* working in a session under a pairing the user has since revoked must not resume pointing at it.
|
|
93728
94738
|
*/
|
|
93729
|
-
restore(
|
|
93730
|
-
for (const entry of
|
|
94739
|
+
restore(entries2, currentGeneration) {
|
|
94740
|
+
for (const entry of entries2) {
|
|
93731
94741
|
if (entry.connectionRevision !== currentGeneration) continue;
|
|
93732
94742
|
this.selected.set(keyOf(entry), entry.sessionId);
|
|
93733
94743
|
}
|
|
@@ -95517,9 +96527,9 @@ function channelPollPlan(provider2) {
|
|
|
95517
96527
|
// `timeout` is the provider's long-poll hold, and it must stay under the executor's own
|
|
95518
96528
|
// deadline (60s) or every poll would look like a transport timeout.
|
|
95519
96529
|
params: { [field]: 0, timeout: 25 },
|
|
95520
|
-
advance: (
|
|
95521
|
-
if (
|
|
95522
|
-
const last = Number(
|
|
96530
|
+
advance: (entries2, _body, current2) => {
|
|
96531
|
+
if (entries2.length === 0) return current2;
|
|
96532
|
+
const last = Number(entries2[entries2.length - 1].transportEventId);
|
|
95523
96533
|
const next = Number.isFinite(last) ? last + 1 : Number(current2[field] ?? 0);
|
|
95524
96534
|
const held = Number(current2[field] ?? 0);
|
|
95525
96535
|
return { ...current2, [field]: contract.cursor.monotonic ? Math.max(held, next) : next };
|
|
@@ -95735,8 +96745,8 @@ function createChannelConnectionLifecycle(deps) {
|
|
|
95735
96745
|
const receive = createChannelReceiveRuntime({
|
|
95736
96746
|
parts: parts2,
|
|
95737
96747
|
canPersistCredentials: () => options.canPersistCredentials(),
|
|
95738
|
-
onWork: (
|
|
95739
|
-
void routeAdmitted(parts2,
|
|
96748
|
+
onWork: (entries2) => {
|
|
96749
|
+
void routeAdmitted(parts2, entries2);
|
|
95740
96750
|
},
|
|
95741
96751
|
onFailure: () => {
|
|
95742
96752
|
if (composition.get(identity.connectionId) === parts2 && !hasMovedOn(identity.connectionId)) registry2.current.setState(identity.connectionId, "failed");
|
|
@@ -95836,15 +96846,21 @@ function createChannelConnectionLifecycle(deps) {
|
|
|
95836
96846
|
message: "the pairing greeting could not be queued"
|
|
95837
96847
|
}));
|
|
95838
96848
|
}
|
|
95839
|
-
async function routeAdmitted(parts2,
|
|
95840
|
-
for (const entry of
|
|
96849
|
+
async function routeAdmitted(parts2, entries2) {
|
|
96850
|
+
for (const entry of entries2) {
|
|
95841
96851
|
const claimed = await options.offerPairingProof({
|
|
95842
96852
|
principal: parts2.identity.principal,
|
|
95843
96853
|
provider: parts2.provider,
|
|
95844
96854
|
connectionId: parts2.identity.connectionId,
|
|
95845
96855
|
event: entry.event
|
|
95846
96856
|
}).catch(() => false);
|
|
95847
|
-
if (!claimed)
|
|
96857
|
+
if (!claimed) {
|
|
96858
|
+
try {
|
|
96859
|
+
options.acknowledgeReceipt?.(parts2, entry);
|
|
96860
|
+
} catch {
|
|
96861
|
+
}
|
|
96862
|
+
continue;
|
|
96863
|
+
}
|
|
95848
96864
|
parts2.queue.transition(entry.id, "executed");
|
|
95849
96865
|
await parts2.journal.record(parts2.queue.get(entry.id) ?? entry).catch(() => void 0);
|
|
95850
96866
|
}
|
|
@@ -95908,6 +96924,22 @@ function createChannelConnectionLifecycle(deps) {
|
|
|
95908
96924
|
botIdentity: (connectionId2) => botIdentities.get(connectionId2) ?? null
|
|
95909
96925
|
};
|
|
95910
96926
|
}
|
|
96927
|
+
function channelReceiptEligible(event, identity, grants) {
|
|
96928
|
+
if (event.sender.bot || event.action || event.command || event.connectionId !== identity.connectionId) return false;
|
|
96929
|
+
const text2 = event.text.trim();
|
|
96930
|
+
if (!text2 || text2.startsWith("/")) return false;
|
|
96931
|
+
const sender2 = event.sender.ref;
|
|
96932
|
+
const granted = grants.find(identity, { connectionId: event.connectionId, senderRef: sender2, conversationRef: event.conversation.ref });
|
|
96933
|
+
if (granted) return granted.revision === event.connectionRevision;
|
|
96934
|
+
return event.invitedMention === true && grants.list(identity).some((grant) => grant.senderRef === sender2 && grant.revision === event.connectionRevision);
|
|
96935
|
+
}
|
|
96936
|
+
function acknowledgeChannelReceipt(parts2, entry, grants) {
|
|
96937
|
+
if (!channelReceiptEligible(entry.event, parts2.identity, grants)) return;
|
|
96938
|
+
parts2.progress.acknowledgeReceipt(entry.id, {
|
|
96939
|
+
destination: entry.event.destination,
|
|
96940
|
+
connectionRevision: entry.event.connectionRevision
|
|
96941
|
+
});
|
|
96942
|
+
}
|
|
95911
96943
|
function createChannelRequestLedger(options) {
|
|
95912
96944
|
function drop(requestId) {
|
|
95913
96945
|
options.approvalBindings.delete(requestId);
|
|
@@ -96654,7 +97686,7 @@ const MAX_IDENTIFIER = 200;
|
|
|
96654
97686
|
const MAX_SECRET = 4096;
|
|
96655
97687
|
const MAX_CHANNEL_SCOPE_LIST = 200;
|
|
96656
97688
|
const MAX_LIST = MAX_CHANNEL_SCOPE_LIST;
|
|
96657
|
-
function record$
|
|
97689
|
+
function record$2(value2, keys2) {
|
|
96658
97690
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
|
|
96659
97691
|
const prototype = Object.getPrototypeOf(value2);
|
|
96660
97692
|
if (prototype !== Object.prototype && prototype !== null) return null;
|
|
@@ -96674,7 +97706,7 @@ function identifierList(value2) {
|
|
|
96674
97706
|
return parsed2.every((entry) => entry !== null) ? parsed2 : null;
|
|
96675
97707
|
}
|
|
96676
97708
|
function parseChannelPairingScope(value2) {
|
|
96677
|
-
const source2 = record$
|
|
97709
|
+
const source2 = record$2(value2, ["allowedProjectIds", "allowedMachineIds", "allowedSessionRefs", "operations"]);
|
|
96678
97710
|
if (!source2) return null;
|
|
96679
97711
|
const projects = identifierList(source2.allowedProjectIds);
|
|
96680
97712
|
const machines2 = identifierList(source2.allowedMachineIds);
|
|
@@ -96707,7 +97739,7 @@ function expectation(source2) {
|
|
|
96707
97739
|
};
|
|
96708
97740
|
}
|
|
96709
97741
|
function parseChannelConnectionWriteInput(value2) {
|
|
96710
|
-
const source2 = record$
|
|
97742
|
+
const source2 = record$2(value2, [
|
|
96711
97743
|
"accountScopeKey",
|
|
96712
97744
|
"epoch",
|
|
96713
97745
|
"expectedConnectionRevision",
|
|
@@ -96721,7 +97753,7 @@ function parseChannelConnectionWriteInput(value2) {
|
|
|
96721
97753
|
const provider2 = parseChannelProvider(source2.provider);
|
|
96722
97754
|
const scope2 = parseChannelPairingScope(source2.scope);
|
|
96723
97755
|
if (!base2 || !provider2 || !scope2) return null;
|
|
96724
|
-
const secretsSource = record$
|
|
97756
|
+
const secretsSource = record$2(source2.secrets, providerSecretFields(provider2));
|
|
96725
97757
|
if (!secretsSource) return null;
|
|
96726
97758
|
const secrets = {};
|
|
96727
97759
|
for (const [field, raw] of Object.entries(secretsSource)) {
|
|
@@ -96730,7 +97762,7 @@ function parseChannelConnectionWriteInput(value2) {
|
|
|
96730
97762
|
}
|
|
96731
97763
|
let agentSelection;
|
|
96732
97764
|
if (source2.agentSelection !== void 0) {
|
|
96733
|
-
const raw = record$
|
|
97765
|
+
const raw = record$2(source2.agentSelection, ["agent", "model", "effort"]);
|
|
96734
97766
|
if (!raw || typeof raw.agent !== "string" || typeof raw.model !== "string" || raw.effort !== null && typeof raw.effort !== "string") return null;
|
|
96735
97767
|
try {
|
|
96736
97768
|
agentSelection = normalizeChannelAgentSelection({ agent: raw.agent, model: raw.model, effort: raw.effort });
|
|
@@ -96741,7 +97773,7 @@ function parseChannelConnectionWriteInput(value2) {
|
|
|
96741
97773
|
return { ...base2, provider: provider2, secrets, scope: scope2, ...agentSelection ? { agentSelection } : {} };
|
|
96742
97774
|
}
|
|
96743
97775
|
function parseChannelBeginPairingInput(value2) {
|
|
96744
|
-
const source2 = record$
|
|
97776
|
+
const source2 = record$2(value2, ["accountScopeKey", "epoch", "expectedConnectionRevision", "provider", "scope"]);
|
|
96745
97777
|
if (!source2) return null;
|
|
96746
97778
|
const base2 = expectation(source2);
|
|
96747
97779
|
const provider2 = parseChannelProvider(source2.provider);
|
|
@@ -96750,7 +97782,7 @@ function parseChannelBeginPairingInput(value2) {
|
|
|
96750
97782
|
return { ...base2, provider: provider2, scope: scope2 };
|
|
96751
97783
|
}
|
|
96752
97784
|
function parseChannelConfirmPairingClaimInput(value2) {
|
|
96753
|
-
const source2 = record$
|
|
97785
|
+
const source2 = record$2(value2, [
|
|
96754
97786
|
"accountScopeKey",
|
|
96755
97787
|
"epoch",
|
|
96756
97788
|
"expectedConnectionRevision",
|
|
@@ -96767,7 +97799,7 @@ function parseChannelConfirmPairingClaimInput(value2) {
|
|
|
96767
97799
|
return { ...base2, provider: provider2, claimHandle, decision: source2.decision };
|
|
96768
97800
|
}
|
|
96769
97801
|
function parseChannelRevokeInput(value2) {
|
|
96770
|
-
const source2 = record$
|
|
97802
|
+
const source2 = record$2(value2, ["accountScopeKey", "epoch", "expectedConnectionRevision", "provider"]);
|
|
96771
97803
|
if (!source2) return null;
|
|
96772
97804
|
const base2 = expectation(source2);
|
|
96773
97805
|
const provider2 = parseChannelProvider(source2.provider);
|
|
@@ -97396,6 +98428,7 @@ function createChannelRequestCoordinator(deps) {
|
|
|
97396
98428
|
canPersistCredentials: () => options.canPersistCredentials(),
|
|
97397
98429
|
...options.botClaimedElsewhere ? { botClaimedElsewhere: options.botClaimedElsewhere } : {},
|
|
97398
98430
|
offerPairingProof: (input) => options.offerPairingProof(input),
|
|
98431
|
+
acknowledgeReceipt: (parts2, entry) => acknowledgeChannelReceipt(parts2, entry, grants),
|
|
97399
98432
|
...options.takeOwedWelcome ? { takeOwedWelcome: options.takeOwedWelcome } : {},
|
|
97400
98433
|
channelAdapters,
|
|
97401
98434
|
generations,
|
|
@@ -98951,9 +99984,9 @@ function parseScope(value2) {
|
|
|
98951
99984
|
}
|
|
98952
99985
|
function parseSecrets(value2) {
|
|
98953
99986
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
|
|
98954
|
-
const
|
|
98955
|
-
if (
|
|
98956
|
-
return Object.fromEntries(
|
|
99987
|
+
const entries2 = Object.entries(value2);
|
|
99988
|
+
if (entries2.length === 0 || !entries2.every(([, secret]) => nonEmptyString(secret))) return null;
|
|
99989
|
+
return Object.fromEntries(entries2);
|
|
98957
99990
|
}
|
|
98958
99991
|
function parseEntry(value2) {
|
|
98959
99992
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
|
|
@@ -100920,8 +101953,8 @@ class ExtensionStore {
|
|
|
100920
101953
|
const pending2 = [];
|
|
100921
101954
|
let totalBytes = 0;
|
|
100922
101955
|
const visit = async (directory, relativeDirectory) => {
|
|
100923
|
-
const
|
|
100924
|
-
for (const entry of
|
|
101956
|
+
const entries2 = (await readdir(directory, { withFileTypes: true })).sort((left, right) => left.name.localeCompare(right.name));
|
|
101957
|
+
for (const entry of entries2) {
|
|
100925
101958
|
const absolutePath = join(directory, entry.name);
|
|
100926
101959
|
const relativePath2 = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
|
|
100927
101960
|
if (entry.isSymbolicLink()) throw new Error(`extension assets contain a symbolic link: ${relativePath2}`);
|
|
@@ -100982,13 +102015,13 @@ class ExtensionStore {
|
|
|
100982
102015
|
}
|
|
100983
102016
|
}
|
|
100984
102017
|
async extract(zip, stagingPath) {
|
|
100985
|
-
const
|
|
100986
|
-
if (
|
|
102018
|
+
const entries2 = Object.values(zip.files);
|
|
102019
|
+
if (entries2.length > (this.options.maxEntries ?? 5e3)) {
|
|
100987
102020
|
throw new Error("extension archive has too many entries");
|
|
100988
102021
|
}
|
|
100989
102022
|
const maxUncompressedBytes = this.options.maxUncompressedBytes ?? 50 * 1024 * 1024;
|
|
100990
102023
|
let extractedBytes = 0;
|
|
100991
|
-
for (const entry of
|
|
102024
|
+
for (const entry of entries2) {
|
|
100992
102025
|
const rawPath = archivePath(entry);
|
|
100993
102026
|
assertSafeArchivePath(rawPath);
|
|
100994
102027
|
assertRegularEntry(entry);
|
|
@@ -101080,33 +102113,33 @@ function createHostSessionKeyStore(input) {
|
|
|
101080
102113
|
return cache2;
|
|
101081
102114
|
}
|
|
101082
102115
|
async function read2() {
|
|
101083
|
-
const
|
|
102116
|
+
const entries2 = /* @__PURE__ */ new Map();
|
|
101084
102117
|
try {
|
|
101085
102118
|
const raw = JSON.parse(await readFile(join(input.dir, FILE), "utf8"));
|
|
101086
|
-
if (typeof raw.sealed !== "string") return
|
|
102119
|
+
if (typeof raw.sealed !== "string") return entries2;
|
|
101087
102120
|
const stored = JSON.parse(input.safeStorage.decryptString(Buffer.from(raw.sealed, "base64")));
|
|
101088
|
-
if (!Array.isArray(stored)) return
|
|
102121
|
+
if (!Array.isArray(stored)) return entries2;
|
|
101089
102122
|
for (const entry of stored) {
|
|
101090
102123
|
const [id2, key] = entry;
|
|
101091
102124
|
if (typeof id2 === "string" && typeof key?.dataEncryptionKey === "string" && typeof key.dek === "string") {
|
|
101092
|
-
|
|
102125
|
+
entries2.set(id2, { dataEncryptionKey: key.dataEncryptionKey, dek: key.dek });
|
|
101093
102126
|
}
|
|
101094
102127
|
}
|
|
101095
102128
|
} catch {
|
|
101096
102129
|
}
|
|
101097
|
-
return
|
|
102130
|
+
return entries2;
|
|
101098
102131
|
}
|
|
101099
102132
|
return {
|
|
101100
102133
|
async get(sessionId) {
|
|
101101
102134
|
return (await load2()).get(sessionId) ?? null;
|
|
101102
102135
|
},
|
|
101103
102136
|
async put(sessionId, key) {
|
|
101104
|
-
const
|
|
101105
|
-
|
|
101106
|
-
|
|
101107
|
-
while (
|
|
102137
|
+
const entries2 = await load2();
|
|
102138
|
+
entries2.delete(sessionId);
|
|
102139
|
+
entries2.set(sessionId, key);
|
|
102140
|
+
while (entries2.size > limit) entries2.delete(entries2.keys().next().value);
|
|
101108
102141
|
const write = writes.then(async () => {
|
|
101109
|
-
const sealed2 = input.safeStorage.encryptString(JSON.stringify([...
|
|
102142
|
+
const sealed2 = input.safeStorage.encryptString(JSON.stringify([...entries2])).toString("base64");
|
|
101110
102143
|
await mkdir(input.dir, { recursive: true, mode: 448 });
|
|
101111
102144
|
const path2 = join(input.dir, FILE);
|
|
101112
102145
|
const staging = `${path2}.${process.pid}.tmp`;
|
|
@@ -102073,91 +103106,166 @@ async function steerAgentSession(transport, sessionId, text2) {
|
|
|
102073
103106
|
return rejectedSteer(error instanceof Error ? error.message : String(error));
|
|
102074
103107
|
}
|
|
102075
103108
|
}
|
|
102076
|
-
|
|
102077
|
-
|
|
102078
|
-
function isReadableFileSync(path2) {
|
|
102079
|
-
try {
|
|
102080
|
-
accessSync(path2, constants$3.R_OK);
|
|
102081
|
-
return statSync(path2).isFile();
|
|
102082
|
-
} catch {
|
|
102083
|
-
return false;
|
|
102084
|
-
}
|
|
103109
|
+
function record$1(value2) {
|
|
103110
|
+
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
102085
103111
|
}
|
|
102086
|
-
function
|
|
103112
|
+
function readObject(path2, read2) {
|
|
102087
103113
|
try {
|
|
102088
|
-
return
|
|
103114
|
+
return record$1(JSON.parse(read2(path2) ?? "null"));
|
|
102089
103115
|
} catch {
|
|
102090
103116
|
return null;
|
|
102091
103117
|
}
|
|
102092
103118
|
}
|
|
102093
|
-
|
|
102094
|
-
const
|
|
102095
|
-
|
|
102096
|
-
|
|
102097
|
-
const
|
|
102098
|
-
|
|
102099
|
-
const
|
|
102100
|
-
|
|
103119
|
+
function localKeys(scope2, read2) {
|
|
103120
|
+
for (const path2 of machineSettingsPaths(scope2)) {
|
|
103121
|
+
const machineId = readObject(path2, read2)?.machineId;
|
|
103122
|
+
if (typeof machineId !== "string" || !machineId) continue;
|
|
103123
|
+
const entries2 = record$1(readObject(join(dirname(path2), "sessions.json"), read2)?.sessions);
|
|
103124
|
+
const keys2 = /* @__PURE__ */ new Map();
|
|
103125
|
+
for (const [id2, value2] of Object.entries(entries2 ?? {})) {
|
|
103126
|
+
const entry = record$1(value2);
|
|
103127
|
+
if (entry?.encryptionVariant !== "dataKey" || record$1(entry.metadata)?.machineId !== machineId) continue;
|
|
103128
|
+
const encoded = entry.encryptionKey;
|
|
103129
|
+
if (typeof encoded !== "string") continue;
|
|
103130
|
+
const key = Buffer.from(encoded, "base64");
|
|
103131
|
+
if (key.length === 32 && key.toString("base64") === encoded) keys2.set(id2, new Uint8Array(key));
|
|
103132
|
+
}
|
|
103133
|
+
return { machineId, keys: keys2 };
|
|
102101
103134
|
}
|
|
102102
|
-
return
|
|
103135
|
+
return null;
|
|
102103
103136
|
}
|
|
102104
|
-
|
|
102105
|
-
let
|
|
102106
|
-
|
|
102107
|
-
|
|
102108
|
-
|
|
102109
|
-
|
|
102110
|
-
|
|
102111
|
-
|
|
102112
|
-
|
|
102113
|
-
|
|
102114
|
-
|
|
102115
|
-
|
|
102116
|
-
|
|
102117
|
-
|
|
103137
|
+
function createAgentSessionReader(context, transport, scope2, read2) {
|
|
103138
|
+
let keys2 = /* @__PURE__ */ new Map();
|
|
103139
|
+
let locked = /* @__PURE__ */ new Set();
|
|
103140
|
+
async function listSessions() {
|
|
103141
|
+
const rows = await context.rest.listSessions();
|
|
103142
|
+
const local = localKeys(scope2, read2);
|
|
103143
|
+
const nextKeys = /* @__PURE__ */ new Map(), nextLocked = /* @__PURE__ */ new Set();
|
|
103144
|
+
const legacyKeys = context.syncCredential.secret ? [decodeBase64$1(context.syncCredential.secret)] : [];
|
|
103145
|
+
const sessions = normalizeSessionRows(rows.filter((row2) => !row2.dataEncryptionKey), legacyKeys);
|
|
103146
|
+
try {
|
|
103147
|
+
for (const row2 of rows) {
|
|
103148
|
+
if (!row2.dataEncryptionKey) continue;
|
|
103149
|
+
nextLocked.add(row2.id);
|
|
103150
|
+
const key = local?.keys.get(row2.id);
|
|
103151
|
+
if (!key) continue;
|
|
103152
|
+
const metadata = row2.metadata ? await decryptDataKeyRecord(key, row2.metadata) : null;
|
|
103153
|
+
if (!metadata || metadata.machineId !== local?.machineId) continue;
|
|
103154
|
+
const state = row2.agentState ? await decryptDataKeyRecord(key, row2.agentState) : null;
|
|
103155
|
+
sessions.push(normalizeDecryptedSessionRow(row2, metadata, state));
|
|
103156
|
+
nextKeys.set(row2.id, new Uint8Array(key));
|
|
103157
|
+
nextLocked.delete(row2.id);
|
|
103158
|
+
}
|
|
103159
|
+
} finally {
|
|
103160
|
+
for (const key of local?.keys.values() ?? []) key.fill(0);
|
|
102118
103161
|
}
|
|
102119
|
-
const
|
|
102120
|
-
|
|
103162
|
+
for (const key of keys2.values()) key.fill(0);
|
|
103163
|
+
keys2 = nextKeys;
|
|
103164
|
+
locked = nextLocked;
|
|
103165
|
+
return sessions.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
103166
|
+
}
|
|
103167
|
+
async function decode2(sessionId, ciphertext) {
|
|
103168
|
+
if (locked.has(sessionId)) return null;
|
|
103169
|
+
const key = keys2.get(sessionId);
|
|
103170
|
+
if (key) return decryptDataKeyJson(key, ciphertext);
|
|
102121
103171
|
try {
|
|
102122
|
-
|
|
103172
|
+
return transport.decodePayload(ciphertext);
|
|
102123
103173
|
} catch {
|
|
103174
|
+
return null;
|
|
102124
103175
|
}
|
|
102125
|
-
}
|
|
102126
|
-
|
|
102127
|
-
|
|
102128
|
-
|
|
102129
|
-
|
|
102130
|
-
|
|
102131
|
-
|
|
102132
|
-
|
|
102133
|
-
|
|
102134
|
-
|
|
103176
|
+
}
|
|
103177
|
+
async function readSequencedMessages(sessionId, limit) {
|
|
103178
|
+
const messages2 = await context.rest.recentMessages(sessionId, limit);
|
|
103179
|
+
const contents = [];
|
|
103180
|
+
for (const message of [...messages2].sort((a, b) => a.seq - b.seq)) {
|
|
103181
|
+
const ciphertext = unwrapEncryptedField(message.content);
|
|
103182
|
+
const content = ciphertext ? await decode2(sessionId, ciphertext) : null;
|
|
103183
|
+
if (content) contents.push({ seq: message.seq, content });
|
|
103184
|
+
}
|
|
103185
|
+
return contents;
|
|
103186
|
+
}
|
|
103187
|
+
async function openStream(sessionId) {
|
|
103188
|
+
let snapshot = createWaitSnapshot(), closed = false, replaying = true;
|
|
103189
|
+
let pending2 = Promise.resolve();
|
|
103190
|
+
const listeners2 = /* @__PURE__ */ new Set();
|
|
103191
|
+
const buffered = [];
|
|
103192
|
+
const publish2 = (next) => {
|
|
103193
|
+
if (closed || next === snapshot) return;
|
|
103194
|
+
snapshot = next;
|
|
103195
|
+
for (const listener of listeners2) listener(next);
|
|
103196
|
+
};
|
|
103197
|
+
const apply = async (update) => {
|
|
103198
|
+
if (closed) return;
|
|
103199
|
+
const message = readNewMessage(update, sessionId);
|
|
103200
|
+
if (message) {
|
|
103201
|
+
const content = await decode2(sessionId, message.ciphertext);
|
|
103202
|
+
if (content) publish2(foldMessageIntoSnapshot(snapshot, content));
|
|
103203
|
+
return;
|
|
103204
|
+
}
|
|
103205
|
+
const state = readSessionAgentState(update, sessionId);
|
|
103206
|
+
const opened = state ? await decode2(sessionId, state) : null;
|
|
103207
|
+
if (opened) publish2(foldAgentStateIntoSnapshot(snapshot, opened));
|
|
103208
|
+
};
|
|
103209
|
+
const unsubscribe = transport.onUpdate((update) => {
|
|
103210
|
+
if (replaying) buffered.push(update);
|
|
103211
|
+
else pending2 = pending2.then(() => apply(update)).catch(() => {
|
|
103212
|
+
});
|
|
103213
|
+
});
|
|
103214
|
+
try {
|
|
103215
|
+
for (const { content } of await readSequencedMessages(sessionId, 50)) snapshot = foldMessageIntoSnapshot(snapshot, content);
|
|
103216
|
+
for (const update of buffered) await apply(update);
|
|
103217
|
+
buffered.length = 0;
|
|
103218
|
+
replaying = false;
|
|
103219
|
+
} catch (error) {
|
|
103220
|
+
closed = true;
|
|
103221
|
+
unsubscribe();
|
|
103222
|
+
throw error;
|
|
102135
103223
|
}
|
|
102136
|
-
|
|
102137
|
-
|
|
102138
|
-
|
|
103224
|
+
return {
|
|
103225
|
+
current: () => snapshot,
|
|
103226
|
+
subscribe: (listener) => {
|
|
103227
|
+
listeners2.add(listener);
|
|
103228
|
+
return () => listeners2.delete(listener);
|
|
103229
|
+
},
|
|
103230
|
+
close: () => {
|
|
103231
|
+
closed = true;
|
|
103232
|
+
listeners2.clear();
|
|
103233
|
+
unsubscribe();
|
|
103234
|
+
}
|
|
103235
|
+
};
|
|
102139
103236
|
}
|
|
102140
|
-
replaying = false;
|
|
102141
|
-
for (const update of buffered) apply(update);
|
|
102142
|
-
buffered.length = 0;
|
|
102143
103237
|
return {
|
|
102144
|
-
|
|
102145
|
-
|
|
102146
|
-
|
|
102147
|
-
|
|
103238
|
+
listSessions,
|
|
103239
|
+
readSequencedMessages,
|
|
103240
|
+
openStream,
|
|
103241
|
+
assertLegacyWrite: (sessionId) => {
|
|
103242
|
+
if (keys2.has(sessionId) || locked.has(sessionId)) throw new Error("Local dataKey session writes require a scoped transport; this command does not support them yet.");
|
|
102148
103243
|
},
|
|
102149
|
-
|
|
102150
|
-
listeners2.clear();
|
|
102151
|
-
unsubscribe();
|
|
102152
|
-
}
|
|
103244
|
+
readMessages: async (sessionId, limit) => (await readSequencedMessages(sessionId, limit)).map((entry) => entry.content)
|
|
102153
103245
|
};
|
|
102154
103246
|
}
|
|
103247
|
+
const SPAWN_RPC_TIMEOUT_MS = 6e4;
|
|
103248
|
+
function isReadableFileSync(path2) {
|
|
103249
|
+
try {
|
|
103250
|
+
accessSync(path2, constants$3.R_OK);
|
|
103251
|
+
return statSync(path2).isFile();
|
|
103252
|
+
} catch {
|
|
103253
|
+
return false;
|
|
103254
|
+
}
|
|
103255
|
+
}
|
|
103256
|
+
function readFileOrNull(path2) {
|
|
103257
|
+
try {
|
|
103258
|
+
return readFileSync(path2, "utf8");
|
|
103259
|
+
} catch {
|
|
103260
|
+
return null;
|
|
103261
|
+
}
|
|
103262
|
+
}
|
|
102155
103263
|
function createAgentRuntime(context, transport, env = process.env) {
|
|
102156
|
-
const keys2 = [decodeBase64$1(context.syncCredential.secret)];
|
|
102157
103264
|
const machineBudget = machineSpawnBudget({ cpuCount: cpus().length, totalMemoryBytes: totalmem() });
|
|
102158
103265
|
const homeDir = context.homeDir, lineageRoot = readAgentEnv(env, machineBudget)?.scope ?? context.homeDir;
|
|
102159
103266
|
const credentialHome = context.credential.source === "saycode" ? null : dirname(context.credential.path);
|
|
102160
103267
|
const now = () => Date.now();
|
|
103268
|
+
const { assertLegacyWrite, ...reader } = createAgentSessionReader(context, transport, { homeDir, env, credentialHome }, readFileOrNull);
|
|
102161
103269
|
return {
|
|
102162
103270
|
env,
|
|
102163
103271
|
homeDir,
|
|
@@ -102167,22 +103275,23 @@ function createAgentRuntime(context, transport, env = process.env) {
|
|
|
102167
103275
|
readFile: readFileOrNull,
|
|
102168
103276
|
modelCatalogReady: () => startCliModelCatalogForContext(context),
|
|
102169
103277
|
isReadableFile: isReadableFileSync,
|
|
102170
|
-
|
|
102171
|
-
|
|
102172
|
-
|
|
102173
|
-
|
|
102174
|
-
|
|
102175
|
-
|
|
102176
|
-
|
|
102177
|
-
|
|
102178
|
-
|
|
102179
|
-
|
|
102180
|
-
|
|
102181
|
-
|
|
102182
|
-
}
|
|
103278
|
+
...reader,
|
|
103279
|
+
sendPrompt: async (sessionId, text2) => {
|
|
103280
|
+
assertLegacyWrite(sessionId);
|
|
103281
|
+
return createCliSessionConnection({
|
|
103282
|
+
rest: context.rest,
|
|
103283
|
+
transport,
|
|
103284
|
+
credential: context.syncCredential,
|
|
103285
|
+
sessionId,
|
|
103286
|
+
// No model/effort override: an agent prompting a sibling must not silently
|
|
103287
|
+
// change that session's configuration.
|
|
103288
|
+
settings: createLiveChatSettings({})
|
|
103289
|
+
}).prompt({ text: text2 });
|
|
103290
|
+
},
|
|
102183
103291
|
spawnSession: async (input) => {
|
|
102184
103292
|
const machineId = readMachineId({ homeDir, env, credentialHome }, readFileOrNull);
|
|
102185
103293
|
if (!machineId) throw new CliError("Could not determine this machine, so no session can be spawned here.");
|
|
103294
|
+
const machineKey = readLocalMachineRpcKey({ homeDir, env, credentialHome }, machineId, readFileOrNull);
|
|
102186
103295
|
const result2 = await transport.rpc(
|
|
102187
103296
|
machineId,
|
|
102188
103297
|
"spawn-happy-session",
|
|
@@ -102197,7 +103306,8 @@ function createAgentRuntime(context, transport, env = process.env) {
|
|
|
102197
103306
|
...inheritedSandboxPolicyEnv(env.HAPPY_PROJECT_SANDBOX_CONFIG)
|
|
102198
103307
|
}
|
|
102199
103308
|
},
|
|
102200
|
-
SPAWN_RPC_TIMEOUT_MS
|
|
103309
|
+
SPAWN_RPC_TIMEOUT_MS,
|
|
103310
|
+
...machineKey ? [machineKey] : []
|
|
102201
103311
|
);
|
|
102202
103312
|
if (!result2?.sessionId) throw new CliError("The machine daemon returned no session id for spawn-happy-session.");
|
|
102203
103313
|
return { sessionId: result2.sessionId };
|
|
@@ -102206,8 +103316,22 @@ function createAgentRuntime(context, transport, env = process.env) {
|
|
|
102206
103316
|
countSpawns: (agentKey, maxSpawn) => spawnBudgetUsage(homeDir, lineageRoot, agentKey, now(), maxSpawn),
|
|
102207
103317
|
recordSpawn: (record2) => appendSpawnRecord(lineageRoot, record2),
|
|
102208
103318
|
readLineage: () => readSpawnRecords(homeDir, lineageRoot),
|
|
102209
|
-
steerSession: (sessionId, text2) =>
|
|
102210
|
-
|
|
103319
|
+
steerSession: async (sessionId, text2) => {
|
|
103320
|
+
try {
|
|
103321
|
+
assertLegacyWrite(sessionId);
|
|
103322
|
+
} catch (error) {
|
|
103323
|
+
return { accepted: false, reason: "rejected", error: String(error) };
|
|
103324
|
+
}
|
|
103325
|
+
return steerAgentSession(transport, sessionId, text2);
|
|
103326
|
+
},
|
|
103327
|
+
stopSession: async (machineId, sessionId) => {
|
|
103328
|
+
try {
|
|
103329
|
+
assertLegacyWrite(sessionId);
|
|
103330
|
+
} catch (error) {
|
|
103331
|
+
return { stopped: false, reason: "rejected", error: String(error) };
|
|
103332
|
+
}
|
|
103333
|
+
return stopAgentSession(transport, machineId, sessionId);
|
|
103334
|
+
},
|
|
102211
103335
|
// Omitted entirely without API access: a Happy `access.key` is sync-only, so
|
|
102212
103336
|
// there is no project list to match against and nothing to register with.
|
|
102213
103337
|
// `--worktree` is gated on the same access for the same reason, and its absence is what
|