@buzzni/saycode-cli 0.7.0 → 0.7.2
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 +914 -601
- package/official-extensions/buzzni.discord-1.1.0.saycode-extension.base64 +1 -0
- package/official-extensions/buzzni.slack-1.1.0.saycode-extension.base64 +1 -0
- package/official-extensions/buzzni.telegram-1.1.0.saycode-extension.base64 +1 -0
- package/official-extensions/catalog.v1.json +26 -0
- package/package.json +2 -1
package/index.mjs
CHANGED
|
@@ -45,7 +45,7 @@ function _mergeNamespaces(n, m) {
|
|
|
45
45
|
}
|
|
46
46
|
return Object.freeze(Object.defineProperty(n, Symbol.toStringTag, { value: "Module" }));
|
|
47
47
|
}
|
|
48
|
-
const version$2 = "0.7.
|
|
48
|
+
const version$2 = "0.7.2";
|
|
49
49
|
const manifest = {
|
|
50
50
|
version: version$2
|
|
51
51
|
};
|
|
@@ -270,8 +270,8 @@ function getCurrentLanguage() {
|
|
|
270
270
|
}
|
|
271
271
|
const workKo = {
|
|
272
272
|
"work.homeTitle": "어떤 작업을 도와드릴까요?",
|
|
273
|
-
"work.new": "새
|
|
274
|
-
"work.archived": "보관된
|
|
273
|
+
"work.new": "새 chat 시작",
|
|
274
|
+
"work.archived": "보관된 chat",
|
|
275
275
|
"work.active": "활성 대화로 돌아가기",
|
|
276
276
|
"work.docx": "문서",
|
|
277
277
|
"work.pdf": "PDF 문서",
|
|
@@ -305,6 +305,85 @@ const workKo = {
|
|
|
305
305
|
"work.xlsxDescription": "표와 수식 작성 및 편집",
|
|
306
306
|
"work.pptxDescription": "발표자료 작성 및 편집"
|
|
307
307
|
};
|
|
308
|
+
const setupFailureHintKo = {
|
|
309
|
+
"setupHint.file-locked": "다른 프로그램이 교체할 파일을 사용 중입니다. 열려 있는 터미널과 Claude·Codex 창을 닫고 다시 시도하세요. 설치된 도구와 로그인은 유지됩니다.",
|
|
310
|
+
"setupHint.npm-missing": "npm을 찾지 못했습니다. Node.js LTS(npm 포함)를 설치한 뒤 다시 시도하세요. Saycode를 다시 시작할 필요는 없습니다.",
|
|
311
|
+
"setupHint.network": "네트워크 또는 프록시 때문에 내려받지 못했습니다. 연결을 확인하고 다시 시도하세요. 회사 프록시를 쓴다면 npm 프록시 설정이 필요할 수 있습니다.",
|
|
312
|
+
"setupHint.permission": "설치 폴더에 쓸 권한이 없습니다. Saycode를 관리자 권한이 아닌 일반 사용자로 실행하고, npm 전역 설치 폴더가 사용자 폴더 안에 있는지 확인하세요.",
|
|
313
|
+
"setupHint.openLogs": "로그 폴더 열기",
|
|
314
|
+
"setupHint.gitMissing": "Git을 찾지 못했습니다. 프로젝트와 작업 사본에 Git이 필요합니다. 설치한 뒤 다시 확인하세요. 다시 시작할 필요는 없습니다.",
|
|
315
|
+
"setupHint.recheck": "다시 확인",
|
|
316
|
+
"setupHint.gitGuide": "Git 설치"
|
|
317
|
+
};
|
|
318
|
+
const usageEntryKo = {
|
|
319
|
+
...setupFailureHintKo,
|
|
320
|
+
"usageChoice.title": "어떻게 사용하시겠어요?",
|
|
321
|
+
"usageChoice.personal": "개인 사용",
|
|
322
|
+
"usageChoice.personalBody": "나만의 프로젝트를 AI와 함께 만들고 관리하세요.",
|
|
323
|
+
"usageChoice.organization": "조직 사용",
|
|
324
|
+
"usageChoice.organizationBody": "팀원과 프로젝트를 공유하고 함께 개발하세요.",
|
|
325
|
+
"onboarding.page.machine": "이 컴퓨터 등록",
|
|
326
|
+
"registration.registered": "등록됨",
|
|
327
|
+
"registration.required": "등록 필요",
|
|
328
|
+
"registration.checking": "확인 중…",
|
|
329
|
+
"registration.registering": "등록 중…",
|
|
330
|
+
"registration.title": "이 컴퓨터 등록",
|
|
331
|
+
"registration.body": "조직에서 사용할 전용 머신으로 이 컴퓨터를 연결하세요.",
|
|
332
|
+
"registration.finish": "조직 작업 시작",
|
|
333
|
+
"registration.organization": "조직",
|
|
334
|
+
"registration.computer": "컴퓨터",
|
|
335
|
+
"registration.thisComputer": "이 컴퓨터",
|
|
336
|
+
"registration.chooseOrganization": "등록할 조직을 선택하세요.",
|
|
337
|
+
"registration.noOrganization": "가입한 조직이 없어요. 조직에 가입한 계정으로 로그인하거나 조직 관리에서 가입 상태를 확인하세요.",
|
|
338
|
+
"registration.selectFailed": "조직을 전환하지 못했어요. 다시 선택해 주세요.",
|
|
339
|
+
"registration.action": "이 컴퓨터 등록",
|
|
340
|
+
"machineSettings.registerThisComputer": "내 컴퓨터 등록",
|
|
341
|
+
"machineSettings.registerMachine": "머신 등록",
|
|
342
|
+
"registration.linked": "이 컴퓨터가 조직에 연결됐어요.",
|
|
343
|
+
"registration.identityPending": "이 컴퓨터의 조직·전용 머신 유형·사용자 배정을 확인 중입니다. 기존 연결은 자동으로 변경하지 않습니다.",
|
|
344
|
+
"localTools.title": "추가 CLI 도구",
|
|
345
|
+
"localTools.body": "Codex와 Claude의 멀티 계정 도구를 함께 준비합니다.",
|
|
346
|
+
"localTools.unavailable": "이 버전에서 로컬 도구 설치를 사용할 수 없어요.",
|
|
347
|
+
"localTools.checking": "확인 중",
|
|
348
|
+
"localTools.missing": "설치 필요",
|
|
349
|
+
"localTools.installing": "설치 중…",
|
|
350
|
+
"localTools.installed": "설치됨",
|
|
351
|
+
"localTools.failed": "설치 실패",
|
|
352
|
+
"localTools.prerequisite": "사전 준비 필요",
|
|
353
|
+
"localTools.prerequisiteBody": "{command}를 설치하고 다시 시도하세요. Saycode를 다시 시작할 필요는 없습니다.",
|
|
354
|
+
"localTools.installGuide": "설치 안내",
|
|
355
|
+
"localTools.retry": "미완료 도구 다시 설치",
|
|
356
|
+
"localTools.install": "추가 도구 설치",
|
|
357
|
+
"localTools.required": "추가 도구 준비 필요",
|
|
358
|
+
"onboarding.checklist": "온보딩 체크리스트",
|
|
359
|
+
"onboarding.checklistBody": "작업에 필요한 도구를 준비하세요. 선택한 단계부터 나머지 설치를 자동으로 진행합니다.",
|
|
360
|
+
"onboarding.autoStart": "이 단계부터 자동 진행",
|
|
361
|
+
"onboarding.autoStop": "자동 진행 중지",
|
|
362
|
+
"onboarding.autoRetry": "자동 진행 다시 시도",
|
|
363
|
+
"onboarding.autoFailed": "자동 진행을 멈췄어요. {detail}",
|
|
364
|
+
"onboarding.autoNeedsAction": "아래 상태를 확인하고 필요한 준비 후 다시 시도하세요.",
|
|
365
|
+
"onboarding.autoWait.organization": "등록할 조직을 선택하면 이어서 진행합니다.",
|
|
366
|
+
"onboarding.autoWait.identity": "이 컴퓨터의 조직·유형·사용자 연결 확인을 기다리고 있습니다.",
|
|
367
|
+
"onboarding.autoWait.machine": "먼저 이 컴퓨터 등록 단계에서 연결을 완료하세요.",
|
|
368
|
+
"onboarding.autoWait.login": "도구 설치는 완료됐어요. Claude와 Codex 계정에 로그인하면 자동으로 이어집니다.",
|
|
369
|
+
"onboarding.autoWait.platform": "이 플랫폼의 AI 인증 확인은 아직 지원되지 않습니다.",
|
|
370
|
+
"onboarding.autoWait.verification": "설치 후 AI 도구의 실행 상태를 다시 확인하고 있습니다.",
|
|
371
|
+
"onboarding.autoWait.cli": "먼저 Saycode CLI 단계에서 도구 준비를 완료하세요.",
|
|
372
|
+
"onboarding.autoWorking": "{step} 준비 중…"
|
|
373
|
+
};
|
|
374
|
+
const homeTabsKo = {
|
|
375
|
+
"homeTabs.greeting": "{name}님, 안녕하세요",
|
|
376
|
+
"homeTabs.label": "홈 모드",
|
|
377
|
+
"homeTabs.project": "Build",
|
|
378
|
+
"homeTabs.work": "Chat",
|
|
379
|
+
"homeTabs.develop": "Develop",
|
|
380
|
+
"homeTabs.new": "새 기획으로 시작",
|
|
381
|
+
"homeTabs.quick": "새 프로젝트 시작",
|
|
382
|
+
"homeTabs.git": "코드 저장소 가져오기",
|
|
383
|
+
"homeTabs.zip": "ZIP/파일 가져오기",
|
|
384
|
+
"homeTabs.folder": "머신 폴더 사용하기",
|
|
385
|
+
"homeTabs.categories": "프로젝트 범주"
|
|
386
|
+
};
|
|
308
387
|
const projectRelocationKO = {
|
|
309
388
|
"projectRelocation.notFound": "이 머신에서 프로젝트 폴더를 확인하지 못했습니다: {path}. 프로젝트를 이 머신으로 복사하거나 프로젝트 설정에서 실제 폴더 경로를 지정하세요.",
|
|
310
389
|
"projectRelocation.ambiguous": "일치하는 프로젝트 폴더가 여러 개입니다: {paths}. 프로젝트 설정에서 사용할 폴더를 지정하세요.",
|
|
@@ -312,7 +391,87 @@ const projectRelocationKO = {
|
|
|
312
391
|
"sessionCreation.dirNotFound": "원격 머신에 디렉토리가 없습니다: {directory}",
|
|
313
392
|
"sessionCreation.workspaceNotSet": "이 머신의 워크스페이스 경로가 설정되지 않았습니다. 머신 워크스페이스를 먼저 설정해주세요."
|
|
314
393
|
};
|
|
394
|
+
const sessionShareKo = {
|
|
395
|
+
"sessionShare.menuAction": "조직에 공유",
|
|
396
|
+
"sessionShare.dialogTitle": "대화 세션 공유",
|
|
397
|
+
"sessionShare.dialogDescription": "내가 속한 조직에 이 대화의 읽기 전용 스냅샷을 공유합니다.",
|
|
398
|
+
"sessionShare.readOnlyScope": "이 대화와 본문에서 참조한 파일의 공유 시점 사본만 공유되며, 다른 프로젝트 파일과 대화는 공개되지 않습니다.",
|
|
399
|
+
"sessionShare.addOrganization": "공유할 조직",
|
|
400
|
+
"sessionShare.selectOrganization": "조직 선택",
|
|
401
|
+
"sessionShare.noOrganizations": "추가로 공유할 수 있는 소속 조직이 없습니다.",
|
|
402
|
+
"sessionShare.currentOrganizations": "현재 공유 중인 조직",
|
|
403
|
+
"sessionShare.noGrants": "아직 공유된 조직이 없습니다.",
|
|
404
|
+
"sessionShare.revokeOrganization": "{organization} 공유 해제",
|
|
405
|
+
"sessionShare.shareAction": "공유",
|
|
406
|
+
"sessionShare.sharedSuccess": "대화가 {organization} 조직에 공유되었습니다",
|
|
407
|
+
"sessionShare.revokedSuccess": "대화의 조직 공유가 해제되었습니다",
|
|
408
|
+
"sessionShare.copyFailed": "공유 링크를 복사하지 못했습니다",
|
|
409
|
+
"sessionShare.copySuccess": "대화 공유 링크가 복사되었습니다",
|
|
410
|
+
"sessionShare.copyLink": "공유 링크 복사",
|
|
411
|
+
"sessionShare.messengerShareOrganization": "{organization} 메신저로 공유",
|
|
412
|
+
"sessionShare.notFound": "공유된 대화를 열 수 없습니다",
|
|
413
|
+
"sessionShare.notFoundDescription": "공유가 해제되었거나 현재 계정이 대상 조직의 활성 멤버가 아닙니다.",
|
|
414
|
+
"sessionShare.loginRequired": "로그인이 필요합니다",
|
|
415
|
+
"sessionShare.loginDescription": "공유 대상 조직의 Saycode 계정으로 로그인해 주세요.",
|
|
416
|
+
"sessionShare.continueLogin": "로그인하고 계속",
|
|
417
|
+
"sessionShare.switchAccount": "다른 계정으로 로그인",
|
|
418
|
+
"sessionShare.loading": "공유된 대화를 불러오는 중…",
|
|
419
|
+
"sessionShare.readOnly": "읽기 전용",
|
|
420
|
+
"sessionShare.fileUnavailable": "공유되지 않은 파일",
|
|
421
|
+
"sessionShare.workspaceUnavailable": "작업공간 정보를 확인한 뒤 다시 공유해 주세요.",
|
|
422
|
+
"sessionShare.tooLong": "공유할 대화가 너무 깁니다.",
|
|
423
|
+
"sessionShare.invalidRequest": "대화 공유 요청이 올바르지 않습니다.",
|
|
424
|
+
"sessionShare.targetMembershipRequired": "소속된 조직에만 공유할 수 있습니다.",
|
|
425
|
+
"sessionShare.requestFailed": "대화 공유 요청을 처리하지 못했습니다.",
|
|
426
|
+
"sessionShare.shareNotFound": "이 대화의 공유 정보를 찾을 수 없습니다.",
|
|
427
|
+
"sessionShare.publicSection": "링크가 있는 누구나",
|
|
428
|
+
"sessionShare.publicToggle": "로그인 없이 링크로 열람 허용",
|
|
429
|
+
"sessionShare.publicDescription": "켜면 링크를 가진 누구나 로그인 없이 이 대화의 공유 시점 사본을 볼 수 있습니다.",
|
|
430
|
+
"sessionShare.publicWarning": "이 링크를 받은 사람은 계정 없이 대화 전문과 참조된 파일을 볼 수 있습니다. 민감한 내용이 없는지 확인해 주세요.",
|
|
431
|
+
"sessionShare.publicLinkLabel": "공개 링크",
|
|
432
|
+
"sessionShare.publicCopyLink": "공개 링크 복사",
|
|
433
|
+
"sessionShare.publicCopySuccess": "공개 링크가 복사되었습니다",
|
|
434
|
+
"sessionShare.publicEnabledSuccess": "대화가 링크 공개로 전환되었습니다",
|
|
435
|
+
"sessionShare.publicDisabledSuccess": "대화의 링크 공개가 해제되었습니다",
|
|
436
|
+
"sessionShare.publicRevokeHint": "공개를 해제하면 이 링크는 영구히 만료되고, 다시 켜면 새 링크가 발급됩니다.",
|
|
437
|
+
"sessionShare.publicNotFoundDescription": "공개가 해제되었거나 링크가 만료되었습니다.",
|
|
438
|
+
"sessionShare.publicRateLimited": "조회 요청이 너무 많습니다. 잠시 후 다시 열어 주세요.",
|
|
439
|
+
"sessionShare.publicUnavailableTitle": "지금은 대화를 열 수 없습니다",
|
|
440
|
+
"sessionShare.publicUnavailable": "지금은 공유된 대화를 불러올 수 없습니다. 잠시 후 다시 시도해 주세요.",
|
|
441
|
+
"sessionShare.rateLimited": "공유 요청이 너무 잦습니다. 잠시 후 다시 시도해 주세요.",
|
|
442
|
+
"sessionShare.snapshotRefreshScope": "공유하거나 공개를 켤 때마다, 이미 공유된 대상이 보는 사본도 지금 대화 내용으로 함께 갱신됩니다.",
|
|
443
|
+
"sessionShare.publicLiveWhileSharing": "공개 링크가 켜져 있습니다. 지금 조직에 공유하면 링크를 가진 사람이 보는 내용도 지금 대화로 갱신됩니다.",
|
|
444
|
+
"sessionShare.grantsLiveWhilePublishing": "이미 {count}개 조직에 공유 중입니다. 공개를 켜면 해당 조직이 보는 사본도 지금 대화로 갱신됩니다."
|
|
445
|
+
};
|
|
446
|
+
const workAreaKo = {
|
|
447
|
+
"workArea.addPanel": "작업 패널 추가",
|
|
448
|
+
"workArea.diff": "변경",
|
|
449
|
+
"workArea.files": "파일",
|
|
450
|
+
"workArea.terminal": "터미널",
|
|
451
|
+
"workArea.browser": "브라우저",
|
|
452
|
+
"workArea.tools": "작업 도구",
|
|
453
|
+
"workArea.hideTools": "도구 접기",
|
|
454
|
+
"workArea.showTools": "도구 펼치기",
|
|
455
|
+
"workArea.resize": "작업 도구 너비 조절",
|
|
456
|
+
"workArea.changedFiles": "변경된 파일",
|
|
457
|
+
"workArea.noChanges": "현재 작업 폴더에서 변경된 파일이 없습니다.",
|
|
458
|
+
"workArea.selectProject": "파일을 보려면 프로젝트를 선택하세요.",
|
|
459
|
+
"workArea.openFile": "파일 열기",
|
|
460
|
+
"workArea.selectFile": "오른쪽 파일 트리에서 파일을 선택하세요.",
|
|
461
|
+
"workArea.newTerminal": "새 터미널",
|
|
462
|
+
"workArea.noTerminal": "터미널을 열어 현재 작업 폴더에서 명령을 실행하세요.",
|
|
463
|
+
"workArea.filterFiles": "파일 필터…",
|
|
464
|
+
"workArea.viewSource": "소스 보기",
|
|
465
|
+
"workArea.preview": "미리보기",
|
|
466
|
+
"workArea.expandFolders": "폴더 펼치기",
|
|
467
|
+
"workArea.collapseFolders": "폴더 접기",
|
|
468
|
+
"workArea.contentSearch": "내용 검색",
|
|
469
|
+
"workArea.splitDiff": "좌우 비교",
|
|
470
|
+
"workArea.unifiedDiff": "한 줄 비교"
|
|
471
|
+
};
|
|
315
472
|
const redesignKo = {
|
|
473
|
+
...sessionShareKo,
|
|
474
|
+
...workAreaKo,
|
|
316
475
|
"ux2.pinnedConversations": "핀된 대화",
|
|
317
476
|
"ux2.pinnedProjects": "핀된 프로젝트",
|
|
318
477
|
"ux2.selectOrganization": "조직 선택",
|
|
@@ -328,6 +487,8 @@ const redesignKo = {
|
|
|
328
487
|
"ux2.remainingUsage": "AI 잔여량",
|
|
329
488
|
"ux2.projectViewOptions": "프로젝트 보기 옵션",
|
|
330
489
|
"ux2.projectTools": "프로젝트 도구",
|
|
490
|
+
"ux2.pinConversation": "대화 고정",
|
|
491
|
+
"ux2.unpinConversation": "대화 고정 해제",
|
|
331
492
|
"ux2.sharingExtensions": "공유·메모리 확장",
|
|
332
493
|
"ux2.conversationFiles": "대화·파일 메뉴",
|
|
333
494
|
"ux2.allProjects": "전체",
|
|
@@ -340,14 +501,10 @@ const redesignKo = {
|
|
|
340
501
|
"ux2.setupDetails": "{step} · 설정 방법",
|
|
341
502
|
"ux2.work": "대화",
|
|
342
503
|
"ux2.workHint": "프로젝트 전체의 대화와 터미널",
|
|
343
|
-
"ux2.createHint": "프로젝트를 어떻게 시작할까요?",
|
|
344
504
|
"ux2.blank": "새 프로젝트",
|
|
345
|
-
"ux2.template": "템플릿에서 시작",
|
|
346
505
|
"ux2.git": "Git 저장소",
|
|
347
506
|
"ux2.zip": "ZIP·파일",
|
|
348
507
|
"ux2.folder": "기존 폴더",
|
|
349
|
-
"ux2.machine": "실행 환경",
|
|
350
|
-
"ux2.machineHint": "프로젝트와 에이전트가 실행될 머신입니다.",
|
|
351
508
|
"ux2.addMachine": "머신 추가",
|
|
352
509
|
"ux2.name": "프로젝트 이름",
|
|
353
510
|
"ux2.choose": "폴더 선택",
|
|
@@ -445,10 +602,10 @@ const machineResourceMetricsKo = {
|
|
|
445
602
|
"machineResourceMetrics.unavailable": "측정 불가 — 연결을 확인해 주세요"
|
|
446
603
|
};
|
|
447
604
|
const hubSessionKo = {
|
|
448
|
-
"hubSession.enable": "
|
|
605
|
+
"hubSession.enable": "프로젝트 허브로 설정",
|
|
449
606
|
"hubSession.disable": "프로젝트 허브 해제",
|
|
450
607
|
"hubSession.hint": "같은 프로젝트의 다른 대화가 턴을 끝내거나 입력을 기다리면 이 대화에 메시지로 알려줍니다",
|
|
451
|
-
"hubSession.newSessionLabel": "
|
|
608
|
+
"hubSession.newSessionLabel": "프로젝트 허브로 설정",
|
|
452
609
|
"hubSession.description": "프로젝트의 작업 세션들을 관리할 수 있습니다. 프로젝트 당 허브는 1개만 설정이 가능합니다.",
|
|
453
610
|
"hubSession.badge": "프로젝트 허브",
|
|
454
611
|
"hubSession.newSessionHint": "다른 대화의 완료·질문을 여기로 모읍니다",
|
|
@@ -1281,6 +1438,7 @@ const personalChatsKo = {
|
|
|
1281
1438
|
"personalChats.title": "내 채팅",
|
|
1282
1439
|
"personalChats.newChat": "새 채팅",
|
|
1283
1440
|
"personalChats.searchPlaceholder": "채팅 검색",
|
|
1441
|
+
"personalChats.sortRecent": "최신 작업순으로 정렬",
|
|
1284
1442
|
"personalChats.empty": "채팅이 없습니다",
|
|
1285
1443
|
"personalChats.emptyHint": "프로젝트를 선택하지 않고 채팅을 시작하세요.",
|
|
1286
1444
|
"personalChats.emptySearch": "검색과 일치하는 채팅이 없습니다.",
|
|
@@ -1807,53 +1965,39 @@ const connectionIndicatorKo = {
|
|
|
1807
1965
|
"connectionIndicator.restored": "연결 복구",
|
|
1808
1966
|
"connectionIndicator.aria": "실시간 동기화 연결 상태: {state}"
|
|
1809
1967
|
};
|
|
1810
|
-
const voiceDictationKo = {
|
|
1811
|
-
"voice.finishAndSend": "받아쓰고 전송",
|
|
1812
|
-
"voice.finishAndSendAria": "녹음을 끝내고 받아쓴 내용을 바로 전송"
|
|
1813
|
-
};
|
|
1814
1968
|
const newSessionProfileKo = {
|
|
1815
1969
|
"newSession.profile.label": "프로필",
|
|
1970
|
+
"newSession.profile.agentHeading": "사용할 AI",
|
|
1971
|
+
"newSession.profile.savedHeading": "AI 프로필",
|
|
1972
|
+
"newSession.profile.savedHint": "선택하면 AI·모델·작업 환경이 함께 바뀝니다",
|
|
1973
|
+
"newSession.profile.saveSetup": "현재 설정 저장",
|
|
1974
|
+
"newSession.profile.manageSetups": "AI 프로필 관리",
|
|
1816
1975
|
"newSession.profile.selectAria": "실행 옵션 프로필",
|
|
1817
1976
|
"newSession.profile.none": "프로필 없음",
|
|
1818
|
-
"newSession.profile.save": "현재 조합 저장",
|
|
1819
|
-
"newSession.profile.saveAria": "현재 실행 옵션을 프로필로 저장",
|
|
1820
1977
|
"newSession.profile.executionSettings": "실행 설정",
|
|
1821
|
-
"newSession.profile.
|
|
1978
|
+
"newSession.profile.editSettings": "편집",
|
|
1979
|
+
"newSession.profile.saveChanges": "변경사항 저장",
|
|
1980
|
+
"newSession.profile.instructionsField": "추가 지시문",
|
|
1981
|
+
"newSession.profile.badgeIconField": "표식 아이콘",
|
|
1982
|
+
"newSession.profile.badgeColorField": "표식 색상",
|
|
1822
1983
|
"newSession.profile.worktreeOn": "작업 사본 사용",
|
|
1823
1984
|
"newSession.profile.worktreeOff": "작업 사본 안 함",
|
|
1824
|
-
"newSession.profile.manage": "프로필 관리",
|
|
1825
|
-
"newSession.profile.nameLabel": "프로필 이름",
|
|
1826
|
-
"newSession.profile.namePlaceholder": "예: 리뷰용",
|
|
1827
|
-
"newSession.profile.confirmSave": "저장",
|
|
1828
1985
|
"newSession.profile.cancel": "취소",
|
|
1829
1986
|
"newSession.profile.saved": "프로필 설정이 변경되었습니다.",
|
|
1830
|
-
"newSession.profile.dirty": "수정됨",
|
|
1831
|
-
"newSession.profile.dirtyTitle": "프로필을 적용한 뒤 옵션을 직접 바꿨습니다.",
|
|
1832
1987
|
"newSession.profile.saveFailed": "프로필을 저장하지 못했습니다.",
|
|
1833
|
-
"newSession.profile.managerTitle": "프로필 관리",
|
|
1988
|
+
"newSession.profile.managerTitle": "AI 프로필 관리",
|
|
1834
1989
|
"newSession.profile.managerEmpty": "저장된 프로필이 없습니다.",
|
|
1835
|
-
"newSession.profile.rename": "이름 변경",
|
|
1836
1990
|
"newSession.profile.delete": "삭제",
|
|
1837
1991
|
"newSession.profile.close": "닫기",
|
|
1838
|
-
"newSession.profile.renameAria": "{name} 이름 변경",
|
|
1839
|
-
"newSession.profile.renameApplyAria": "{name} 이름 변경 적용",
|
|
1840
1992
|
"newSession.profile.deleteAria": "{name} 삭제",
|
|
1841
1993
|
"newSession.profile.fallbackAgent": "{value} 에이전트를 지금 쓸 수 없어 현재 선택을 유지했습니다.",
|
|
1842
1994
|
"newSession.profile.fallbackModel": "{value} 모델이 목록에 없어 기본값으로 시작합니다.",
|
|
1843
1995
|
"newSession.profile.fallbackEffort": "{value} 강도를 이 모델에서 쓸 수 없어 기본값으로 시작합니다.",
|
|
1844
1996
|
"newSession.profile.fallbackWorktree": "이 프로젝트는 git 저장소가 아니라 작업 사본을 끕니다.",
|
|
1845
1997
|
// specs/desktop-session-personas T9
|
|
1846
|
-
// rename/renameAria 와 같은 규칙 — 가시 텍스트에 이름을 넣으면 버튼 폭이 행마다 달라진다.
|
|
1847
|
-
"newSession.profile.editPersona": "페르소나 편집",
|
|
1848
|
-
"newSession.profile.editPersonaAria": "{name} 페르소나 편집",
|
|
1849
|
-
"newSession.profile.instructionsLabel": "{name} 지시문",
|
|
1850
1998
|
"newSession.profile.instructionsPlaceholder": "예: 리뷰 관점에서만 답하라",
|
|
1851
1999
|
"newSession.profile.instructionsTooLong": "지시문이 너무 깁니다 ({max}자 이하로 줄여주세요).",
|
|
1852
|
-
"newSession.profile.
|
|
1853
|
-
"newSession.profile.skillsLabel": "배포할 스킬",
|
|
1854
|
-
"newSession.profile.badgeIconLabel": "{name} 표식 아이콘",
|
|
1855
|
-
"newSession.profile.badgeColorLabel": "{name} 표식 색상",
|
|
1856
|
-
"newSession.profile.badgeSave": "{name} 표식 저장"
|
|
2000
|
+
"newSession.profile.skillsLabel": "배포할 스킬"
|
|
1857
2001
|
};
|
|
1858
2002
|
const agentBoardBroadcastKo = {
|
|
1859
2003
|
"agentBoard.broadcastSelected": "선택됨",
|
|
@@ -2075,21 +2219,24 @@ const composerAddMenuKo = {
|
|
|
2075
2219
|
"shortcuts.action.composerAttachFileDescription": "포커스된 메시지 작성란의 파일 선택기를 엽니다."
|
|
2076
2220
|
};
|
|
2077
2221
|
const composerQuickCommandsKo = {
|
|
2078
|
-
"composer.quickCommands.title": "자주
|
|
2079
|
-
"composer.quickCommands.
|
|
2222
|
+
"composer.quickCommands.title": "자주 쓰는 프롬프트",
|
|
2223
|
+
"composer.quickCommands.description": "선택하면 입력창에 추가됩니다",
|
|
2224
|
+
"composer.quickCommands.emptyDraftHint": "입력창에 문장을 쓰면 저장할 수 있습니다.",
|
|
2080
2225
|
"composer.quickCommands.trigger": "Quick Commands",
|
|
2081
|
-
"composer.quickCommands.
|
|
2082
|
-
"composer.quickCommands.
|
|
2226
|
+
"composer.quickCommands.saveScope": "현재 입력 저장",
|
|
2227
|
+
"composer.quickCommands.saveCurrent": "이 프로젝트에 저장",
|
|
2228
|
+
"composer.quickCommands.saveCurrentGlobal": "모든 프로젝트에 저장",
|
|
2083
2229
|
"composer.quickCommands.storageWriteFailed": "이 기기에 저장하지 못했습니다 — 앱을 다시 시작하면 목록이 사라질 수 있습니다.",
|
|
2084
|
-
"composer.quickCommands.empty": "
|
|
2230
|
+
"composer.quickCommands.empty": "아직 저장한 프롬프트가 없습니다.",
|
|
2085
2231
|
"composer.quickCommands.projectScope": "이 프로젝트",
|
|
2086
2232
|
"composer.quickCommands.globalScope": "모든 프로젝트",
|
|
2087
|
-
"composer.quickCommands.remove": '"{label}" 삭제'
|
|
2233
|
+
"composer.quickCommands.remove": '"{label}" 삭제',
|
|
2234
|
+
"composer.quickCommands.rename": '"{label}" 이름 변경'
|
|
2088
2235
|
};
|
|
2089
2236
|
const promptHistoryKo = {
|
|
2090
2237
|
"composer.promptHistory.trigger": "입력 기록",
|
|
2091
2238
|
"composer.promptHistory.triggerMac": "입력 기록 (⌃R)",
|
|
2092
|
-
"composer.promptHistory.search": "
|
|
2239
|
+
"composer.promptHistory.search": "프롬프트 검색",
|
|
2093
2240
|
"composer.promptHistory.empty": "아직 보낸 프롬프트가 없습니다.",
|
|
2094
2241
|
"composer.promptHistory.noMatch": "일치하는 기록이 없습니다.",
|
|
2095
2242
|
"composer.promptHistory.count": "{count}건 · 이 기기에만 저장",
|
|
@@ -2495,7 +2642,6 @@ const catalogExtensionsKo = {
|
|
|
2495
2642
|
...sessionTokenUsageKo,
|
|
2496
2643
|
...worktreeSetupKo,
|
|
2497
2644
|
...connectionIndicatorKo,
|
|
2498
|
-
...voiceDictationKo,
|
|
2499
2645
|
...newSessionProfileKo,
|
|
2500
2646
|
...agentBoardBroadcastKo,
|
|
2501
2647
|
...judgementEvidenceKo,
|
|
@@ -2543,6 +2689,7 @@ const onboardingKo = {
|
|
|
2543
2689
|
"login.dialogTitle": "Saycode 계정으로 로그인",
|
|
2544
2690
|
"login.dialogDescription": "로컬 데이터는 이 컴퓨터에 그대로 남고, 현재 창만 클라우드 작업공간으로 전환됩니다.",
|
|
2545
2691
|
"login.dialogClose": "로그인 닫기",
|
|
2692
|
+
"login.loginDevServer": "개발 서버에 로그인",
|
|
2546
2693
|
"guestRecovery.title": "로컬 작업공간을 준비하지 못했어요",
|
|
2547
2694
|
"guestRecovery.body": "Saycode가 로컬 런타임을 시작하지 못했습니다. 다시 시도하거나 계정으로 로그인해 클라우드 작업공간을 사용하세요.",
|
|
2548
2695
|
"guestRecovery.retry": "다시 시도",
|
|
@@ -2744,6 +2891,8 @@ const recoveryPasskeyKo = {
|
|
|
2744
2891
|
};
|
|
2745
2892
|
const ko = {
|
|
2746
2893
|
...workKo,
|
|
2894
|
+
...usageEntryKo,
|
|
2895
|
+
...homeTabsKo,
|
|
2747
2896
|
"orgRouting.sharedInput": "이 입력은 조직의 지정 분류 머신에서 처리했습니다.",
|
|
2748
2897
|
"orgRouting.title": "조직 공유 자동 모드",
|
|
2749
2898
|
"orgRouting.ready": "분류 머신 준비됨",
|
|
@@ -2839,14 +2988,12 @@ const ko = {
|
|
|
2839
2988
|
"happyCliUpdate.checkRetry": "다시 확인",
|
|
2840
2989
|
"happyCliUpdate.openManager": "머신 보기",
|
|
2841
2990
|
"happyCliUpdate.retry": "다시 시도",
|
|
2991
|
+
"happyCliUpdate.snoozeOneDay": "하루동안 안보기",
|
|
2842
2992
|
"happyCliUpdate.dismiss": "닫기",
|
|
2843
2993
|
"happyCliUpdate.onlineEmpty": "온라인 머신이 없습니다.",
|
|
2844
2994
|
"happyCliUpdate.showMore": "{count}대 더보기",
|
|
2845
2995
|
"happyCliUpdate.showLess": "접기",
|
|
2846
2996
|
"happyCliUpdate.updateAll": "{count}대 모두 업데이트",
|
|
2847
|
-
"happyCliUpdate.confirmAll": "{count}대 업데이트",
|
|
2848
|
-
"happyCliUpdate.confirmAllNotice": "{count}대의 연결이 잠시 끊길 수 있습니다.",
|
|
2849
|
-
"happyCliUpdate.cancel": "취소",
|
|
2850
2997
|
// Boot screen
|
|
2851
2998
|
"boot.preparing": "Saycode 준비 중...",
|
|
2852
2999
|
"boot.stage.credentials": "로그인 상태 확인 중…",
|
|
@@ -2898,9 +3045,12 @@ const ko = {
|
|
|
2898
3045
|
"account.syncing": "캐시된 워크스페이스를 최신 정보로 동기화하는 중입니다.",
|
|
2899
3046
|
"sidebar.newProject": "새 프로젝트",
|
|
2900
3047
|
"sidebar.modeChats": "대화",
|
|
2901
|
-
"sidebar.modeProjects": "
|
|
3048
|
+
"sidebar.modeProjects": "Project",
|
|
2902
3049
|
"sidebar.modeAria": "사이드바 보기",
|
|
2903
3050
|
"sidebar.primaryNavAria": "주요 탐색",
|
|
3051
|
+
"sidebar.saycodeHome": "Saycode 홈",
|
|
3052
|
+
"sidebar.workBoard": "작업 보드",
|
|
3053
|
+
"sidebar.externalIntegrations": "외부 연동",
|
|
2904
3054
|
"sidebar.more": "더보기",
|
|
2905
3055
|
"sidebar.moreAria": "추가 탐색",
|
|
2906
3056
|
"sidebar.allConversations": "전체 대화",
|
|
@@ -2922,7 +3072,6 @@ const ko = {
|
|
|
2922
3072
|
"sidebarPanel.newChatAria": "{name} 프로젝트에서 새 대화 시작",
|
|
2923
3073
|
"home.eyebrow": "워크스페이스 홈",
|
|
2924
3074
|
"home.newProject": "새 프로젝트",
|
|
2925
|
-
"home.searchActive": "‘{query}’ 검색 결과 · 프로젝트 {count}개",
|
|
2926
3075
|
"detailTab.chat": "대화",
|
|
2927
3076
|
"detailTab.sessions": "대화 목록",
|
|
2928
3077
|
"chat.machineOnline": "머신 온라인",
|
|
@@ -2954,10 +3103,6 @@ const ko = {
|
|
|
2954
3103
|
"newProject.nameLabel": "프로젝트 이름",
|
|
2955
3104
|
"newProject.existingFolderLabel": "기존 폴더",
|
|
2956
3105
|
"newProject.choose": "선택",
|
|
2957
|
-
"newProject.summaryBlank": "빈 동기화 프로젝트를 만듭니다.",
|
|
2958
|
-
"newProject.summaryGit": "프로젝트를 만들고 선택한 머신에서 Git 가져오기를 시작합니다.",
|
|
2959
|
-
"newProject.summaryZip": "프로젝트를 만들고 선택한 머신에서 선택한 ZIP/파일을 가져옵니다.",
|
|
2960
|
-
"newProject.summaryExistingFolder": "기존 절대 경로 폴더를 동기화 프로젝트로 등록합니다.",
|
|
2961
3106
|
"newProject.creating": "만드는 중...",
|
|
2962
3107
|
"newProject.createAction": "프로젝트 만들기",
|
|
2963
3108
|
"newProject.importAction": "프로젝트 가져오기",
|
|
@@ -2983,8 +3128,6 @@ const ko = {
|
|
|
2983
3128
|
"composer.attachmentPreviewOpen": "{name} 크게 보기",
|
|
2984
3129
|
"composer.attachmentPreviewDialog": "첨부 이미지 미리보기",
|
|
2985
3130
|
"composer.attachmentPreviewClose": "첨부 이미지 미리보기 닫기",
|
|
2986
|
-
"composer.voiceLiveTitle": "실시간 음성 인식",
|
|
2987
|
-
"composer.voiceLiveHint": "말씀하시면 인식된 문장이 여기에 바로 표시됩니다.",
|
|
2988
3131
|
"upload.kindImage": "이미지",
|
|
2989
3132
|
"upload.kindFile": "파일",
|
|
2990
3133
|
"upload.tooLarge": '"{name}"이(가) 너무 큽니다. 최대 {limit}MB까지 첨부할 수 있습니다.',
|
|
@@ -4235,7 +4378,6 @@ const ko = {
|
|
|
4235
4378
|
// Session reconnect / interrupt / delete flow
|
|
4236
4379
|
"session.recreatedButAssignFailed": "대화는 다시 만들었지만 프로젝트 연결 확인에 실패했습니다. {detail}",
|
|
4237
4380
|
"session.continuedNew": "기존 대화 연결이 끊겨 새 대화로 이어서 보냈습니다.",
|
|
4238
|
-
"session.reactivated": "기존 대화를 다시 활성화했습니다.",
|
|
4239
4381
|
"session.reconnectNoMachine": "대화를 다시 연결할 머신 정보를 찾을 수 없습니다.",
|
|
4240
4382
|
"session.cannotReconnect": "이 대화는 다시 연결할 수 없어 기존 요청에 답변할 수 없습니다. 새 대화에서 이어가세요.",
|
|
4241
4383
|
"session.recreateNoProject": "대화를 다시 만들 프로젝트 정보를 찾을 수 없습니다.",
|
|
@@ -4420,15 +4562,9 @@ const ko = {
|
|
|
4420
4562
|
"machinePicker.label": "머신",
|
|
4421
4563
|
"machinePicker.addMachine": "머신 추가",
|
|
4422
4564
|
"machinePicker.noMachines": "등록된 머신이 없습니다. 먼저 머신을 등록해 주세요.",
|
|
4423
|
-
"machinePicker.
|
|
4565
|
+
"machinePicker.noOnlineMachines": "온라인 머신이 없습니다. 머신을 켜거나 연결해 주세요.",
|
|
4424
4566
|
"machinePicker.online": "온라인",
|
|
4425
4567
|
"machinePicker.offline": "지금 꺼져 있음",
|
|
4426
|
-
"machinePicker.recommended": "추천",
|
|
4427
|
-
"machinePicker.reasonOnlyOnline": "온라인인 유일한 머신이에요",
|
|
4428
|
-
"machinePicker.reasonLeastLoaded": "가장 여유 있는 머신이에요",
|
|
4429
|
-
"machinePicker.loadLight": "여유 있음",
|
|
4430
|
-
"machinePicker.loadModerate": "보통",
|
|
4431
|
-
"machinePicker.loadHeavy": "혼잡",
|
|
4432
4568
|
// Machine dialog / deploy URL bar / project detail / memory layer
|
|
4433
4569
|
"machineDialog.selectOnline": "새 대화를 시작할 온라인 머신을 선택하세요.",
|
|
4434
4570
|
"deployUrlBar.copyUrl": "URL 복사",
|
|
@@ -4466,7 +4602,6 @@ const ko = {
|
|
|
4466
4602
|
"projectSection.communityTitle": "프로젝트 커뮤니티",
|
|
4467
4603
|
"projectSection.communityLoading": "커뮤니티 프로젝트를 불러오는 중입니다...",
|
|
4468
4604
|
"projectSection.communityEmpty": "공개된 커뮤니티 프로젝트가 없습니다.",
|
|
4469
|
-
"projectSection.searchEmpty": "검색 결과가 없습니다.",
|
|
4470
4605
|
"preview.tokenFailedStatus": "Preview 토큰 발급 실패 ({status})",
|
|
4471
4606
|
"preview.tokenFailed": "Preview 토큰 발급에 실패했습니다.",
|
|
4472
4607
|
"preview.tokenUrlInvalid": "Preview 토큰 URL이 올바르지 않습니다.",
|
|
@@ -4555,7 +4690,7 @@ const ko = {
|
|
|
4555
4690
|
"chat.noSessionTitle": "대화가 없습니다",
|
|
4556
4691
|
"chat.noSessionBody": "이 프로젝트에는 아직 연결된 대화가 없습니다.",
|
|
4557
4692
|
"chat.loadingOlder": "불러오는 중...",
|
|
4558
|
-
"chat.loadOlder": "이전 대화
|
|
4693
|
+
"chat.loadOlder": "이전 대화",
|
|
4559
4694
|
"chat.search.newer": "새로운 결과",
|
|
4560
4695
|
"chat.search.close": "찾기 닫기",
|
|
4561
4696
|
"chat.search.partial": "이전 메시지 더 있음",
|
|
@@ -4606,11 +4741,6 @@ const ko = {
|
|
|
4606
4741
|
"agentActivity.waiting": "응답을 기다리는 중",
|
|
4607
4742
|
"agentActivity.logLines": "로그 {count}줄",
|
|
4608
4743
|
"agentActivity.elapsed": "경과 {duration}",
|
|
4609
|
-
"agentActivity.took": "{duration} 소요",
|
|
4610
|
-
"agentActivity.complete": "응답 완료",
|
|
4611
|
-
"agentActivity.refreshAria": "대화 새로고침",
|
|
4612
|
-
"agentActivity.refreshTitle": "대화 영역만 새로고침",
|
|
4613
|
-
"agentActivity.refresh": "새로고침",
|
|
4614
4744
|
"toolCall.answerSubmitted": "답변 제출됨",
|
|
4615
4745
|
"toolCall.originalTool": "원본 도구",
|
|
4616
4746
|
"toolCall.resultImages": "도구가 돌려준 이미지",
|
|
@@ -4647,7 +4777,6 @@ const ko = {
|
|
|
4647
4777
|
"templates.required": "시작할 템플릿을 선택해 주세요.",
|
|
4648
4778
|
"templates.onlineOnly": "템플릿 프로젝트는 온라인 머신에서만 시작할 수 있습니다.",
|
|
4649
4779
|
"templates.unknown": "선택한 템플릿을 찾을 수 없습니다.",
|
|
4650
|
-
"templates.summary": "검증된 템플릿 파일로 프로젝트를 만들고, 첫 프롬프트를 입력창에 채웁니다.",
|
|
4651
4780
|
"templates.importedNotice": "'{name}' 프로젝트를 템플릿에서 만들었습니다. 파일 {count}개를 준비했고 첫 프롬프트를 입력창에 채웠습니다.",
|
|
4652
4781
|
"templates.partialNotice": "'{name}' 프로젝트는 생성했지만 템플릿 적용이 완전하지 않습니다. {detail}",
|
|
4653
4782
|
"templates.galleryLabel": "프로젝트 템플릿",
|
|
@@ -4818,7 +4947,7 @@ const ko = {
|
|
|
4818
4947
|
"workspace.loadLatestFailed": "최신 워크스페이스 정보를 불러오지 못했습니다.",
|
|
4819
4948
|
"workspace.refreshFailed": "워크스페이스를 새로고침하지 못했습니다.",
|
|
4820
4949
|
"workspace.responseStalled": "에이전트가 {seconds}초 넘게 새 출력을 내지 않고 있습니다. 긴 작업(빌드·테스트) 중일 수 있어 응답 대기를 유지합니다.",
|
|
4821
|
-
"workspace.syncPausedNetwork": "네트워크 문제로 응답 동기화가 중단되었습니다. 연결
|
|
4950
|
+
"workspace.syncPausedNetwork": "네트워크 문제로 응답 동기화가 중단되었습니다. 네트워크 연결 상태를 확인해 주세요.",
|
|
4822
4951
|
"workspace.syncResumed": "네트워크가 복구되어 응답 동기화가 재개되었습니다.",
|
|
4823
4952
|
"workspace.syncDelayedNetwork": "네트워크 문제로 응답 동기화가 지연되고 있습니다. 연결이 복구되면 자동으로 이어집니다.",
|
|
4824
4953
|
"workspace.syncStoppedNoSession": "대화 메시지를 찾을 수 없어 응답 대기를 중지했습니다. 새 대화에서 이어가세요.",
|
|
@@ -5063,6 +5192,23 @@ const ko = {
|
|
|
5063
5192
|
"connectors.apiKeySaving": "검증 중…",
|
|
5064
5193
|
"connectors.apiKeyFailed": "API Key를 확인하지 못했습니다. 키와 meetings:read 권한을 확인해 주세요.",
|
|
5065
5194
|
"connectors.googleOrgOnly": "Google 커넥터는 회사 계정으로만 연결할 수 있습니다.",
|
|
5195
|
+
"integrations.title": "연동",
|
|
5196
|
+
"integrations.description": "즐겨 쓰는 서비스와 Saycode를 연결해 대화에서 활용하세요.",
|
|
5197
|
+
"integrations.localDescription": "이 기기에서 사용할 확장을 설치하고 관리하세요. 온라인 계정 연동은 Cloud 모드에서 사용할 수 있습니다.",
|
|
5198
|
+
"integrations.connected": "연결됨",
|
|
5199
|
+
"integrations.noConnections": "아직 연결된 서비스가 없습니다.",
|
|
5200
|
+
"integrations.available": "사용 가능한 연동",
|
|
5201
|
+
"integrations.search": "연동 검색",
|
|
5202
|
+
"integrations.noResults": "검색 결과가 없습니다.",
|
|
5203
|
+
"integrations.statusFailed": "커넥터 상태를 확인하지 못했습니다. 새로고침해서 다시 시도하세요.",
|
|
5204
|
+
"integrations.confirmDisconnect": "연결 해제 확인",
|
|
5205
|
+
"integrations.provider.notion": "문서와 위키를 검색하고 불러옵니다.",
|
|
5206
|
+
"integrations.provider.slack": "채널과 메시지를 검색하고 읽습니다.",
|
|
5207
|
+
"integrations.provider.google-drive": "Drive 파일을 검색하고 읽습니다.",
|
|
5208
|
+
"integrations.provider.gmail": "메일과 스레드를 검색하고 읽습니다.",
|
|
5209
|
+
"integrations.provider.knoi": "회의와 기록을 검색하고 읽습니다.",
|
|
5210
|
+
"integrations.provider.github": "저장소 가져오기와 PR 작업에 사용합니다.",
|
|
5211
|
+
"integrations.provider.gitlab": "저장소 가져오기와 PR 작업에 사용합니다.",
|
|
5066
5212
|
"remoteDir.emptyResponse": "머신이 빈 디렉토리 목록 응답을 반환했습니다.",
|
|
5067
5213
|
"remoteDir.listFailed": "원격 디렉토리 목록을 불러오지 못했습니다.",
|
|
5068
5214
|
"remoteFile.sha256Failed": "SHA-256를 계산할 수 없습니다 (crypto.subtle 사용 불가).",
|
|
@@ -5078,7 +5224,7 @@ const ko = {
|
|
|
5078
5224
|
"codexRecovery.limit-reached": "적용한 계정도 사용 한도에 도달했습니다. 여유가 있는 계정으로 로그인한 뒤 다시 적용하세요.",
|
|
5079
5225
|
"codexRecovery.probe.locked": "이 대화의 암호화 키를 사용할 수 없습니다. Saycode 로그인 상태와 대화 접근 권한을 확인하세요.",
|
|
5080
5226
|
"codexRecovery.probe.connection": "대화 실행 프로세스에 연결하지 못했습니다. 연결 상태를 확인하고 상태를 다시 조회하세요.",
|
|
5081
|
-
"codexRecovery.probe.missing": "이 대화의 실행 프로세스가 복구 요청을 받지 못했습니다. CLI 설치와 별개로 기존 프로세스가 남아 있거나 종료됐을 수 있습니다. 대기 중인 대화라면
|
|
5227
|
+
"codexRecovery.probe.missing": "이 대화의 실행 프로세스가 복구 요청을 받지 못했습니다. CLI 설치와 별개로 기존 프로세스가 남아 있거나 종료됐을 수 있습니다. 대기 중인 대화라면 메시지를 보내 재개한 뒤 상태를 다시 확인하세요.",
|
|
5082
5228
|
"codexRecovery.probe.invalid": "복구 상태 응답을 해석하지 못했습니다. 대화의 암호화 상태와 실행 중인 런타임의 호환성을 확인하세요.",
|
|
5083
5229
|
"codexRecovery.probe.changed": "조회 중 로그인 또는 대화 암호화 상태가 바뀌었습니다. 상태를 다시 확인하세요.",
|
|
5084
5230
|
"codexRecovery.continuePrompt": "이어서 진행해줘.",
|
|
@@ -5148,14 +5294,8 @@ const ko = {
|
|
|
5148
5294
|
...previewTunnelKo,
|
|
5149
5295
|
...recoveryPasskeyKo,
|
|
5150
5296
|
// Composer / ask-user / workspace defaults + git status (non-hook + App composer)
|
|
5151
|
-
"composer.endedSessionNotice": "이 대화는 잠시 대기 중이에요. 메시지를 보내면 자동으로 이어서 계속됩니다.",
|
|
5152
5297
|
"session.badge.standby": "대기",
|
|
5153
5298
|
"session.badge.done": "완료",
|
|
5154
|
-
"composer.endedSessionAria": "종료된 대화 복구",
|
|
5155
|
-
"composer.continueConversation": "대화 계속하기",
|
|
5156
|
-
"composer.continuingConversation": "대화 복구 중...",
|
|
5157
|
-
"composer.startNewConversation": "새 대화 시작",
|
|
5158
|
-
"composer.recoveryFailed": "기존 대화를 다시 열지 못했습니다. 새 대화를 시작해 이어가세요. ({detail})",
|
|
5159
5299
|
"askUser.otherLabel": "기타 (직접 입력)",
|
|
5160
5300
|
"askUser.questionFallback": "질문 {index}",
|
|
5161
5301
|
"age.unknown": "알 수 없음",
|
|
@@ -12696,7 +12836,7 @@ function normalizeConnectors(raw) {
|
|
|
12696
12836
|
}
|
|
12697
12837
|
return out;
|
|
12698
12838
|
}
|
|
12699
|
-
const PROVIDERS
|
|
12839
|
+
const PROVIDERS = ["claude", "codex"];
|
|
12700
12840
|
const ROTATION_STATES = [
|
|
12701
12841
|
"stopped",
|
|
12702
12842
|
"starting",
|
|
@@ -12734,7 +12874,7 @@ function nullableNum(value2) {
|
|
|
12734
12874
|
return typeof value2 === "number" && Number.isFinite(value2) ? value2 : null;
|
|
12735
12875
|
}
|
|
12736
12876
|
function provider(value2) {
|
|
12737
|
-
return PROVIDERS
|
|
12877
|
+
return PROVIDERS.includes(str$1(value2)) ? str$1(value2) : null;
|
|
12738
12878
|
}
|
|
12739
12879
|
function parseBundle(value2) {
|
|
12740
12880
|
const row2 = record$8(value2);
|
|
@@ -13660,7 +13800,7 @@ function defaultEffortForAgent(agent, model) {
|
|
|
13660
13800
|
const newSessionAgents = [
|
|
13661
13801
|
{ id: "claude", label: "Claude", provider: "Anthropic" },
|
|
13662
13802
|
{ id: "codex", label: "Codex", provider: "OpenAI" },
|
|
13663
|
-
{ id: "opencode", label: "
|
|
13803
|
+
{ id: "opencode", label: "Opencode", provider: "opencode" },
|
|
13664
13804
|
{ id: "grok", label: "Grok", provider: "xAI" }
|
|
13665
13805
|
];
|
|
13666
13806
|
function parseStarterAgent(value2) {
|
|
@@ -22621,9 +22761,9 @@ function resolvedProjectPreviewRelayUrl(input) {
|
|
|
22621
22761
|
}
|
|
22622
22762
|
return `${input.syncBaseUrl}/v1/preview/${encodeURIComponent(input.machineId)}/${input.port}/?ptoken=${encodeURIComponent(response.token)}`;
|
|
22623
22763
|
}
|
|
22624
|
-
const BASE64_PATTERN$
|
|
22764
|
+
const BASE64_PATTERN$3 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
22625
22765
|
function versionedAutomationBase64(value2, maxBytes, exactBytes) {
|
|
22626
|
-
if (typeof value2 !== "string" || !value2 || value2.length > Math.ceil(maxBytes / 3) * 4 || !BASE64_PATTERN$
|
|
22766
|
+
if (typeof value2 !== "string" || !value2 || value2.length > Math.ceil(maxBytes / 3) * 4 || !BASE64_PATTERN$3.test(value2)) {
|
|
22627
22767
|
throw new Error("automation-response-invalid");
|
|
22628
22768
|
}
|
|
22629
22769
|
const decoded = Buffer$1.from(value2, "base64");
|
|
@@ -29858,6 +29998,7 @@ async function captureCliVoicePrompt(context, voiceFile, waitForStop) {
|
|
|
29858
29998
|
}
|
|
29859
29999
|
}
|
|
29860
30000
|
const USAGE_SETTINGS_NAVIGATION_DESTINATIONS = [
|
|
30001
|
+
"settings_onboarding",
|
|
29861
30002
|
"settings_profile",
|
|
29862
30003
|
"settings_system_prompt",
|
|
29863
30004
|
"settings_personal_memory",
|
|
@@ -29927,6 +30068,7 @@ const USAGE_NAVIGATION_DESTINATIONS = [
|
|
|
29927
30068
|
"all_conversations",
|
|
29928
30069
|
"machines",
|
|
29929
30070
|
"organization_home",
|
|
30071
|
+
"integrations",
|
|
29930
30072
|
"settings",
|
|
29931
30073
|
"organization_admin",
|
|
29932
30074
|
"system_admin",
|
|
@@ -32951,9 +33093,9 @@ function channelText(text2) {
|
|
|
32951
33093
|
}
|
|
32952
33094
|
const ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
32953
33095
|
const RAW_ED25519_PUBLIC_KEY_BYTES = 32;
|
|
32954
|
-
const BASE64_PATTERN$
|
|
33096
|
+
const BASE64_PATTERN$2 = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
32955
33097
|
function createExtensionCatalogVerifier(publicKeyBase64) {
|
|
32956
|
-
if (!publicKeyBase64 || !BASE64_PATTERN$
|
|
33098
|
+
if (!publicKeyBase64 || !BASE64_PATTERN$2.test(publicKeyBase64) || publicKeyBase64.length % 4 !== 0) {
|
|
32957
33099
|
throw new Error("extension catalog public key must be base64");
|
|
32958
33100
|
}
|
|
32959
33101
|
const raw = Buffer.from(publicKeyBase64, "base64");
|
|
@@ -32976,14 +33118,14 @@ function createExtensionCatalogVerifier(publicKeyBase64) {
|
|
|
32976
33118
|
const MAX_EXTENSION_CATALOG_PAYLOAD_BYTES = 256 * 1024;
|
|
32977
33119
|
const EXTENSION_CATALOG_SCHEMA_VERSION = 2;
|
|
32978
33120
|
const RELEASE_URL_PREFIX = "https://github.com/buzzni/saycode-extensions/releases/";
|
|
32979
|
-
const ID_PATTERN$
|
|
32980
|
-
const SEMVER_PATTERN$
|
|
32981
|
-
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
32982
|
-
const BASE64_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
33121
|
+
const ID_PATTERN$4 = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
|
|
33122
|
+
const SEMVER_PATTERN$2 = /^\d+\.\d+\.\d+$/;
|
|
33123
|
+
const SHA256_PATTERN$1 = /^[a-f0-9]{64}$/;
|
|
33124
|
+
const BASE64_PATTERN$1 = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
32983
33125
|
const ISO_INSTANT_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
|
|
32984
33126
|
const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
32985
33127
|
function decodeBase64(value2, path) {
|
|
32986
|
-
if (!BASE64_PATTERN.test(value2) || value2.length % 4 !== 0) fail$7(path, "expected base64");
|
|
33128
|
+
if (!BASE64_PATTERN$1.test(value2) || value2.length % 4 !== 0) fail$7(path, "expected base64");
|
|
32987
33129
|
const padding = value2.endsWith("==") ? 2 : value2.endsWith("=") ? 1 : 0;
|
|
32988
33130
|
const bytes = new Uint8Array(value2.length / 4 * 3 - padding);
|
|
32989
33131
|
let offset = 0;
|
|
@@ -33022,16 +33164,16 @@ function releaseUrl(value2, path) {
|
|
|
33022
33164
|
}
|
|
33023
33165
|
return parsed2;
|
|
33024
33166
|
}
|
|
33025
|
-
function descriptor(value2, path) {
|
|
33167
|
+
function descriptor$1(value2, path) {
|
|
33026
33168
|
const record2 = objectWithExactKeys(value2, ["id", "version", "releaseUrl", "archiveUrl", "sha256"], path);
|
|
33027
|
-
const id2 = text$3(record2.id, `${path}.id`, ID_PATTERN$
|
|
33169
|
+
const id2 = text$3(record2.id, `${path}.id`, ID_PATTERN$4);
|
|
33028
33170
|
if (id2.includes("..")) fail$7(`${path}.id`, "malformed value");
|
|
33029
33171
|
return {
|
|
33030
33172
|
id: id2,
|
|
33031
|
-
version: text$3(record2.version, `${path}.version`, SEMVER_PATTERN$
|
|
33173
|
+
version: text$3(record2.version, `${path}.version`, SEMVER_PATTERN$2),
|
|
33032
33174
|
releaseUrl: releaseUrl(record2.releaseUrl, `${path}.releaseUrl`),
|
|
33033
33175
|
archiveUrl: releaseUrl(record2.archiveUrl, `${path}.archiveUrl`),
|
|
33034
|
-
sha256: text$3(record2.sha256, `${path}.sha256`, SHA256_PATTERN)
|
|
33176
|
+
sha256: text$3(record2.sha256, `${path}.sha256`, SHA256_PATTERN$1)
|
|
33035
33177
|
};
|
|
33036
33178
|
}
|
|
33037
33179
|
function revocation(value2, path) {
|
|
@@ -33039,13 +33181,13 @@ function revocation(value2, path) {
|
|
|
33039
33181
|
const record2 = value2;
|
|
33040
33182
|
const keys2 = Object.keys(record2).sort().join(",");
|
|
33041
33183
|
if (keys2 !== "id,version" && keys2 !== "id,reason,version") fail$7(path, "unexpected fields");
|
|
33042
|
-
const id2 = text$3(record2.id, `${path}.id`, ID_PATTERN$
|
|
33184
|
+
const id2 = text$3(record2.id, `${path}.id`, ID_PATTERN$4);
|
|
33043
33185
|
if (id2.includes("..")) fail$7(`${path}.id`, "malformed value");
|
|
33044
33186
|
return {
|
|
33045
33187
|
id: id2,
|
|
33046
33188
|
// Exact versions only: ADR-048 has no wildcard, so a mistaken entry costs one version,
|
|
33047
33189
|
// not an entire extension.
|
|
33048
|
-
version: text$3(record2.version, `${path}.version`, SEMVER_PATTERN$
|
|
33190
|
+
version: text$3(record2.version, `${path}.version`, SEMVER_PATTERN$2),
|
|
33049
33191
|
...record2.reason === void 0 ? {} : { reason: text$3(record2.reason, `${path}.reason`, /^.{1,200}$/s) }
|
|
33050
33192
|
};
|
|
33051
33193
|
}
|
|
@@ -33061,7 +33203,7 @@ function parsePayload(value2) {
|
|
|
33061
33203
|
if (record2.schemaVersion !== EXTENSION_CATALOG_SCHEMA_VERSION) {
|
|
33062
33204
|
fail$7("catalog.schemaVersion", `unsupported catalog schema ${String(record2.schemaVersion)}`);
|
|
33063
33205
|
}
|
|
33064
|
-
const extensions = array$2(record2.extensions, "catalog.extensions").map((entry, index) => descriptor(entry, `catalog.extensions[${index}]`));
|
|
33206
|
+
const extensions = array$2(record2.extensions, "catalog.extensions").map((entry, index) => descriptor$1(entry, `catalog.extensions[${index}]`));
|
|
33065
33207
|
if (new Set(extensions.map((entry) => entry.id)).size !== extensions.length) {
|
|
33066
33208
|
fail$7("catalog.extensions", "duplicate extension id");
|
|
33067
33209
|
}
|
|
@@ -33704,17 +33846,19 @@ function createHostController(deps) {
|
|
|
33704
33846
|
};
|
|
33705
33847
|
}
|
|
33706
33848
|
async function reconcileOne(spec, deps) {
|
|
33707
|
-
if (deps.isRevoked(spec.extensionId, spec.version)) return "revoked";
|
|
33708
33849
|
let current = deps.registry.get(spec.extensionId);
|
|
33709
|
-
|
|
33710
|
-
|
|
33711
|
-
|
|
33712
|
-
|
|
33713
|
-
|
|
33850
|
+
const target = current?.installedVersion === spec.version ? spec.version : await deps.official.versionOf(spec.extensionId);
|
|
33851
|
+
if (!target) return "unavailable";
|
|
33852
|
+
if (deps.isRevoked(spec.extensionId, target)) return "revoked";
|
|
33853
|
+
if (current?.installedVersion !== target) {
|
|
33854
|
+
const installed = await deps.store.install(await deps.official.archive(spec.extensionId));
|
|
33855
|
+
if (installed.id !== spec.extensionId || installed.version !== target) return "failed";
|
|
33856
|
+
current = await deps.registry.apply(spec.extensionId, current ? { type: "update-installed", version: target } : { type: "installed", version: target });
|
|
33714
33857
|
}
|
|
33715
33858
|
if (!current) return "failed";
|
|
33716
33859
|
if (!current.enabled) current = await deps.registry.apply(spec.extensionId, { type: "enabled" }) ?? current;
|
|
33717
|
-
const
|
|
33860
|
+
const declared = new Set((await deps.store.readInstalledManifest(spec.extensionId, target)).permissions);
|
|
33861
|
+
const wanted = new Set(spec.approvedPermissions.filter((permission) => declared.has(permission)));
|
|
33718
33862
|
for (const permission of current.approvedPermissions) {
|
|
33719
33863
|
if (!wanted.has(permission)) await deps.registry.apply(spec.extensionId, { type: "permission-revoked", permission });
|
|
33720
33864
|
}
|
|
@@ -33740,474 +33884,9 @@ async function reconcileHostExtensions(desired, deps) {
|
|
|
33740
33884
|
}
|
|
33741
33885
|
return result;
|
|
33742
33886
|
}
|
|
33743
|
-
function describe(secretKey) {
|
|
33744
|
-
const { publicKey } = nacl.box.keyPair.fromSecretKey(secretKey);
|
|
33745
|
-
const digest = createHash("sha256").update(publicKey).digest("hex").slice(0, 16).toUpperCase();
|
|
33746
|
-
return {
|
|
33747
|
-
publicKey,
|
|
33748
|
-
secretKey,
|
|
33749
|
-
publicKeyBase64: Buffer.from(publicKey).toString("base64"),
|
|
33750
|
-
fingerprint: digest.match(/.{4}/g).join("-")
|
|
33751
|
-
};
|
|
33752
|
-
}
|
|
33753
|
-
async function openChannelHostKey(input) {
|
|
33754
|
-
if (!input.safeStorage.isEncryptionAvailable()) return null;
|
|
33755
|
-
const path = join(input.dir, "host-key.v1.json");
|
|
33756
|
-
let text2 = null;
|
|
33757
|
-
try {
|
|
33758
|
-
text2 = await readFile(path, "utf8");
|
|
33759
|
-
} catch (error) {
|
|
33760
|
-
if (error.code !== "ENOENT") throw error;
|
|
33761
|
-
}
|
|
33762
|
-
if (text2 !== null) {
|
|
33763
|
-
const parsed2 = JSON.parse(text2);
|
|
33764
|
-
if (parsed2.v !== 1 || typeof parsed2.sealed !== "string") throw new Error("channel host key file is unreadable");
|
|
33765
|
-
const secret = Buffer.from(input.safeStorage.decryptString(Buffer.from(parsed2.sealed, "base64")), "base64");
|
|
33766
|
-
if (secret.length !== nacl.box.secretKeyLength) throw new Error("channel host key file is unreadable");
|
|
33767
|
-
return describe(new Uint8Array(secret));
|
|
33768
|
-
}
|
|
33769
|
-
const { secretKey } = nacl.box.keyPair();
|
|
33770
|
-
await mkdir(input.dir, { recursive: true, mode: 448 });
|
|
33771
|
-
const sealed2 = input.safeStorage.encryptString(Buffer.from(secretKey).toString("base64")).toString("base64");
|
|
33772
|
-
const staging = `${path}.${process.pid}.tmp`;
|
|
33773
|
-
await writeFile(staging, JSON.stringify({ v: 1, sealed: sealed2 }), { mode: 384 });
|
|
33774
|
-
await rename(staging, path);
|
|
33775
|
-
return describe(secretKey);
|
|
33776
|
-
}
|
|
33777
|
-
function defaultIsAlive(pid) {
|
|
33778
|
-
try {
|
|
33779
|
-
process.kill(pid, 0);
|
|
33780
|
-
return true;
|
|
33781
|
-
} catch (error) {
|
|
33782
|
-
return error.code === "EPERM";
|
|
33783
|
-
}
|
|
33784
|
-
}
|
|
33785
|
-
async function acquireChannelHostLock(input) {
|
|
33786
|
-
const path = join(input.dir, "host.lock");
|
|
33787
|
-
const isAlive = input.isAlive ?? defaultIsAlive;
|
|
33788
|
-
for (let attempt2 = 0; attempt2 < 2; attempt2 += 1) {
|
|
33789
|
-
try {
|
|
33790
|
-
const handle = await open(path, "wx", 384);
|
|
33791
|
-
await handle.writeFile(JSON.stringify({ pid: process.pid, startedAt: Date.now() }));
|
|
33792
|
-
await handle.close();
|
|
33793
|
-
return {
|
|
33794
|
-
kind: "held",
|
|
33795
|
-
release: async () => {
|
|
33796
|
-
const current = await readFile(path, "utf8").then((text2) => JSON.parse(text2), () => null);
|
|
33797
|
-
if (current?.pid === process.pid) await rm(path, { force: true });
|
|
33798
|
-
}
|
|
33799
|
-
};
|
|
33800
|
-
} catch (error) {
|
|
33801
|
-
if (error.code !== "EEXIST") throw error;
|
|
33802
|
-
}
|
|
33803
|
-
let holder = null;
|
|
33804
|
-
try {
|
|
33805
|
-
const parsed2 = JSON.parse(await readFile(path, "utf8"));
|
|
33806
|
-
holder = typeof parsed2.pid === "number" && Number.isSafeInteger(parsed2.pid) ? parsed2.pid : null;
|
|
33807
|
-
} catch {
|
|
33808
|
-
holder = null;
|
|
33809
|
-
}
|
|
33810
|
-
if (holder === null || isAlive(holder)) return { kind: "refused" };
|
|
33811
|
-
await rm(path, { force: true });
|
|
33812
|
-
}
|
|
33813
|
-
return { kind: "refused" };
|
|
33814
|
-
}
|
|
33815
|
-
function isBytes(a) {
|
|
33816
|
-
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
|
|
33817
|
-
}
|
|
33818
|
-
function abytes(value2, length, title2 = "") {
|
|
33819
|
-
const bytes = isBytes(value2);
|
|
33820
|
-
const len = value2?.length;
|
|
33821
|
-
const needsLen = length !== void 0;
|
|
33822
|
-
if (!bytes || needsLen) {
|
|
33823
|
-
const prefix = title2 && `"${title2}" `;
|
|
33824
|
-
const ofLen = "";
|
|
33825
|
-
const got = bytes ? `length=${len}` : `type=${typeof value2}`;
|
|
33826
|
-
const message = prefix + "expected Uint8Array" + ofLen + ", got " + got;
|
|
33827
|
-
if (!bytes)
|
|
33828
|
-
throw new TypeError(message);
|
|
33829
|
-
throw new RangeError(message);
|
|
33830
|
-
}
|
|
33831
|
-
return value2;
|
|
33832
|
-
}
|
|
33833
|
-
function aexists(instance, checkFinished = true) {
|
|
33834
|
-
if (instance.destroyed)
|
|
33835
|
-
throw new Error("Hash instance has been destroyed");
|
|
33836
|
-
if (checkFinished && instance.finished)
|
|
33837
|
-
throw new Error("Hash#digest() has already been called");
|
|
33838
|
-
}
|
|
33839
|
-
function aoutput(out, instance) {
|
|
33840
|
-
abytes(out, void 0, "digestInto() output");
|
|
33841
|
-
const min = instance.outputLen;
|
|
33842
|
-
if (out.length < min) {
|
|
33843
|
-
throw new RangeError('"digestInto() output" expected to be of length >=' + min);
|
|
33844
|
-
}
|
|
33845
|
-
}
|
|
33846
|
-
function clean(...arrays) {
|
|
33847
|
-
for (let i = 0; i < arrays.length; i++) {
|
|
33848
|
-
arrays[i].fill(0);
|
|
33849
|
-
}
|
|
33850
|
-
}
|
|
33851
|
-
function createView(arr) {
|
|
33852
|
-
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
33853
|
-
}
|
|
33854
|
-
function rotr(word, shift) {
|
|
33855
|
-
return word << 32 - shift | word >>> shift;
|
|
33856
|
-
}
|
|
33857
|
-
const hasHexBuiltin = /* @__PURE__ */ (() => (
|
|
33858
|
-
// @ts-ignore
|
|
33859
|
-
typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function"
|
|
33860
|
-
))();
|
|
33861
|
-
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
33862
|
-
function bytesToHex(bytes) {
|
|
33863
|
-
abytes(bytes);
|
|
33864
|
-
if (hasHexBuiltin)
|
|
33865
|
-
return bytes.toHex();
|
|
33866
|
-
let hex = "";
|
|
33867
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
33868
|
-
hex += hexes[bytes[i]];
|
|
33869
|
-
}
|
|
33870
|
-
return hex;
|
|
33871
|
-
}
|
|
33872
|
-
function createHasher(hashCons, info = {}) {
|
|
33873
|
-
const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
|
|
33874
|
-
const tmp = hashCons(void 0);
|
|
33875
|
-
hashC.outputLen = tmp.outputLen;
|
|
33876
|
-
hashC.blockLen = tmp.blockLen;
|
|
33877
|
-
hashC.canXOF = tmp.canXOF;
|
|
33878
|
-
hashC.create = (opts) => hashCons(opts);
|
|
33879
|
-
Object.assign(hashC, info);
|
|
33880
|
-
return Object.freeze(hashC);
|
|
33881
|
-
}
|
|
33882
|
-
const oidNist = (suffix) => ({
|
|
33883
|
-
// Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.
|
|
33884
|
-
// Larger suffix values would need base-128 OID encoding and a different length byte.
|
|
33885
|
-
oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
|
|
33886
|
-
});
|
|
33887
|
-
function Chi(a, b, c) {
|
|
33888
|
-
return a & b ^ ~a & c;
|
|
33889
|
-
}
|
|
33890
|
-
function Maj(a, b, c) {
|
|
33891
|
-
return a & b ^ a & c ^ b & c;
|
|
33892
|
-
}
|
|
33893
|
-
class HashMD {
|
|
33894
|
-
blockLen;
|
|
33895
|
-
outputLen;
|
|
33896
|
-
canXOF = false;
|
|
33897
|
-
padOffset;
|
|
33898
|
-
isLE;
|
|
33899
|
-
// For partial updates less than block size
|
|
33900
|
-
buffer;
|
|
33901
|
-
view;
|
|
33902
|
-
finished = false;
|
|
33903
|
-
length = 0;
|
|
33904
|
-
pos = 0;
|
|
33905
|
-
destroyed = false;
|
|
33906
|
-
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
33907
|
-
this.blockLen = blockLen;
|
|
33908
|
-
this.outputLen = outputLen;
|
|
33909
|
-
this.padOffset = padOffset;
|
|
33910
|
-
this.isLE = isLE;
|
|
33911
|
-
this.buffer = new Uint8Array(blockLen);
|
|
33912
|
-
this.view = createView(this.buffer);
|
|
33913
|
-
}
|
|
33914
|
-
update(data) {
|
|
33915
|
-
aexists(this);
|
|
33916
|
-
abytes(data);
|
|
33917
|
-
const { view, buffer, blockLen } = this;
|
|
33918
|
-
const len = data.length;
|
|
33919
|
-
for (let pos = 0; pos < len; ) {
|
|
33920
|
-
const take = Math.min(blockLen - this.pos, len - pos);
|
|
33921
|
-
if (take === blockLen) {
|
|
33922
|
-
const dataView = createView(data);
|
|
33923
|
-
for (; blockLen <= len - pos; pos += blockLen)
|
|
33924
|
-
this.process(dataView, pos);
|
|
33925
|
-
continue;
|
|
33926
|
-
}
|
|
33927
|
-
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
33928
|
-
this.pos += take;
|
|
33929
|
-
pos += take;
|
|
33930
|
-
if (this.pos === blockLen) {
|
|
33931
|
-
this.process(view, 0);
|
|
33932
|
-
this.pos = 0;
|
|
33933
|
-
}
|
|
33934
|
-
}
|
|
33935
|
-
this.length += data.length;
|
|
33936
|
-
this.roundClean();
|
|
33937
|
-
return this;
|
|
33938
|
-
}
|
|
33939
|
-
digestInto(out) {
|
|
33940
|
-
aexists(this);
|
|
33941
|
-
aoutput(out, this);
|
|
33942
|
-
this.finished = true;
|
|
33943
|
-
const { buffer, view, blockLen, isLE } = this;
|
|
33944
|
-
let { pos } = this;
|
|
33945
|
-
buffer[pos++] = 128;
|
|
33946
|
-
clean(this.buffer.subarray(pos));
|
|
33947
|
-
if (this.padOffset > blockLen - pos) {
|
|
33948
|
-
this.process(view, 0);
|
|
33949
|
-
pos = 0;
|
|
33950
|
-
}
|
|
33951
|
-
for (let i = pos; i < blockLen; i++)
|
|
33952
|
-
buffer[i] = 0;
|
|
33953
|
-
view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);
|
|
33954
|
-
this.process(view, 0);
|
|
33955
|
-
const oview = createView(out);
|
|
33956
|
-
const len = this.outputLen;
|
|
33957
|
-
if (len % 4)
|
|
33958
|
-
throw new Error("_sha2: outputLen must be aligned to 32bit");
|
|
33959
|
-
const outLen = len / 4;
|
|
33960
|
-
const state = this.get();
|
|
33961
|
-
if (outLen > state.length)
|
|
33962
|
-
throw new Error("_sha2: outputLen bigger than state");
|
|
33963
|
-
for (let i = 0; i < outLen; i++)
|
|
33964
|
-
oview.setUint32(4 * i, state[i], isLE);
|
|
33965
|
-
}
|
|
33966
|
-
digest() {
|
|
33967
|
-
const { buffer, outputLen } = this;
|
|
33968
|
-
this.digestInto(buffer);
|
|
33969
|
-
const res = buffer.slice(0, outputLen);
|
|
33970
|
-
this.destroy();
|
|
33971
|
-
return res;
|
|
33972
|
-
}
|
|
33973
|
-
_cloneInto(to) {
|
|
33974
|
-
to ||= new this.constructor();
|
|
33975
|
-
to.set(...this.get());
|
|
33976
|
-
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
33977
|
-
to.destroyed = destroyed;
|
|
33978
|
-
to.finished = finished;
|
|
33979
|
-
to.length = length;
|
|
33980
|
-
to.pos = pos;
|
|
33981
|
-
if (length % blockLen)
|
|
33982
|
-
to.buffer.set(buffer);
|
|
33983
|
-
return to;
|
|
33984
|
-
}
|
|
33985
|
-
clone() {
|
|
33986
|
-
return this._cloneInto();
|
|
33987
|
-
}
|
|
33988
|
-
}
|
|
33989
|
-
const SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|
33990
|
-
1779033703,
|
|
33991
|
-
3144134277,
|
|
33992
|
-
1013904242,
|
|
33993
|
-
2773480762,
|
|
33994
|
-
1359893119,
|
|
33995
|
-
2600822924,
|
|
33996
|
-
528734635,
|
|
33997
|
-
1541459225
|
|
33998
|
-
]);
|
|
33999
|
-
const SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
34000
|
-
1116352408,
|
|
34001
|
-
1899447441,
|
|
34002
|
-
3049323471,
|
|
34003
|
-
3921009573,
|
|
34004
|
-
961987163,
|
|
34005
|
-
1508970993,
|
|
34006
|
-
2453635748,
|
|
34007
|
-
2870763221,
|
|
34008
|
-
3624381080,
|
|
34009
|
-
310598401,
|
|
34010
|
-
607225278,
|
|
34011
|
-
1426881987,
|
|
34012
|
-
1925078388,
|
|
34013
|
-
2162078206,
|
|
34014
|
-
2614888103,
|
|
34015
|
-
3248222580,
|
|
34016
|
-
3835390401,
|
|
34017
|
-
4022224774,
|
|
34018
|
-
264347078,
|
|
34019
|
-
604807628,
|
|
34020
|
-
770255983,
|
|
34021
|
-
1249150122,
|
|
34022
|
-
1555081692,
|
|
34023
|
-
1996064986,
|
|
34024
|
-
2554220882,
|
|
34025
|
-
2821834349,
|
|
34026
|
-
2952996808,
|
|
34027
|
-
3210313671,
|
|
34028
|
-
3336571891,
|
|
34029
|
-
3584528711,
|
|
34030
|
-
113926993,
|
|
34031
|
-
338241895,
|
|
34032
|
-
666307205,
|
|
34033
|
-
773529912,
|
|
34034
|
-
1294757372,
|
|
34035
|
-
1396182291,
|
|
34036
|
-
1695183700,
|
|
34037
|
-
1986661051,
|
|
34038
|
-
2177026350,
|
|
34039
|
-
2456956037,
|
|
34040
|
-
2730485921,
|
|
34041
|
-
2820302411,
|
|
34042
|
-
3259730800,
|
|
34043
|
-
3345764771,
|
|
34044
|
-
3516065817,
|
|
34045
|
-
3600352804,
|
|
34046
|
-
4094571909,
|
|
34047
|
-
275423344,
|
|
34048
|
-
430227734,
|
|
34049
|
-
506948616,
|
|
34050
|
-
659060556,
|
|
34051
|
-
883997877,
|
|
34052
|
-
958139571,
|
|
34053
|
-
1322822218,
|
|
34054
|
-
1537002063,
|
|
34055
|
-
1747873779,
|
|
34056
|
-
1955562222,
|
|
34057
|
-
2024104815,
|
|
34058
|
-
2227730452,
|
|
34059
|
-
2361852424,
|
|
34060
|
-
2428436474,
|
|
34061
|
-
2756734187,
|
|
34062
|
-
3204031479,
|
|
34063
|
-
3329325298
|
|
34064
|
-
]);
|
|
34065
|
-
const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
34066
|
-
class SHA2_32B extends HashMD {
|
|
34067
|
-
constructor(outputLen) {
|
|
34068
|
-
super(64, outputLen, 8, false);
|
|
34069
|
-
}
|
|
34070
|
-
get() {
|
|
34071
|
-
const { A, B, C, D, E, F, G, H } = this;
|
|
34072
|
-
return [A, B, C, D, E, F, G, H];
|
|
34073
|
-
}
|
|
34074
|
-
// prettier-ignore
|
|
34075
|
-
set(A, B, C, D, E, F, G, H) {
|
|
34076
|
-
this.A = A | 0;
|
|
34077
|
-
this.B = B | 0;
|
|
34078
|
-
this.C = C | 0;
|
|
34079
|
-
this.D = D | 0;
|
|
34080
|
-
this.E = E | 0;
|
|
34081
|
-
this.F = F | 0;
|
|
34082
|
-
this.G = G | 0;
|
|
34083
|
-
this.H = H | 0;
|
|
34084
|
-
}
|
|
34085
|
-
process(view, offset) {
|
|
34086
|
-
for (let i = 0; i < 16; i++, offset += 4)
|
|
34087
|
-
SHA256_W[i] = view.getUint32(offset, false);
|
|
34088
|
-
for (let i = 16; i < 64; i++) {
|
|
34089
|
-
const W15 = SHA256_W[i - 15];
|
|
34090
|
-
const W2 = SHA256_W[i - 2];
|
|
34091
|
-
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
|
|
34092
|
-
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
|
|
34093
|
-
SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
|
|
34094
|
-
}
|
|
34095
|
-
let { A, B, C, D, E, F, G, H } = this;
|
|
34096
|
-
for (let i = 0; i < 64; i++) {
|
|
34097
|
-
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
|
34098
|
-
const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
|
|
34099
|
-
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
|
|
34100
|
-
const T2 = sigma0 + Maj(A, B, C) | 0;
|
|
34101
|
-
H = G;
|
|
34102
|
-
G = F;
|
|
34103
|
-
F = E;
|
|
34104
|
-
E = D + T1 | 0;
|
|
34105
|
-
D = C;
|
|
34106
|
-
C = B;
|
|
34107
|
-
B = A;
|
|
34108
|
-
A = T1 + T2 | 0;
|
|
34109
|
-
}
|
|
34110
|
-
A = A + this.A | 0;
|
|
34111
|
-
B = B + this.B | 0;
|
|
34112
|
-
C = C + this.C | 0;
|
|
34113
|
-
D = D + this.D | 0;
|
|
34114
|
-
E = E + this.E | 0;
|
|
34115
|
-
F = F + this.F | 0;
|
|
34116
|
-
G = G + this.G | 0;
|
|
34117
|
-
H = H + this.H | 0;
|
|
34118
|
-
this.set(A, B, C, D, E, F, G, H);
|
|
34119
|
-
}
|
|
34120
|
-
roundClean() {
|
|
34121
|
-
clean(SHA256_W);
|
|
34122
|
-
}
|
|
34123
|
-
destroy() {
|
|
34124
|
-
this.destroyed = true;
|
|
34125
|
-
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|
34126
|
-
clean(this.buffer);
|
|
34127
|
-
}
|
|
34128
|
-
}
|
|
34129
|
-
class _SHA256 extends SHA2_32B {
|
|
34130
|
-
// We cannot use array here since array allows indexing by variable
|
|
34131
|
-
// which means optimizer/compiler cannot use registers.
|
|
34132
|
-
A = SHA256_IV[0] | 0;
|
|
34133
|
-
B = SHA256_IV[1] | 0;
|
|
34134
|
-
C = SHA256_IV[2] | 0;
|
|
34135
|
-
D = SHA256_IV[3] | 0;
|
|
34136
|
-
E = SHA256_IV[4] | 0;
|
|
34137
|
-
F = SHA256_IV[5] | 0;
|
|
34138
|
-
G = SHA256_IV[6] | 0;
|
|
34139
|
-
H = SHA256_IV[7] | 0;
|
|
34140
|
-
constructor() {
|
|
34141
|
-
super(32);
|
|
34142
|
-
}
|
|
34143
|
-
}
|
|
34144
|
-
const sha256 = /* @__PURE__ */ createHasher(
|
|
34145
|
-
() => new _SHA256(),
|
|
34146
|
-
/* @__PURE__ */ oidNist(1)
|
|
34147
|
-
);
|
|
34148
|
-
function channelLeaseSubject(input) {
|
|
34149
|
-
const material = JSON.stringify([input.provider, input.installationId]);
|
|
34150
|
-
return bytesToHex(sha256(new TextEncoder().encode(material)));
|
|
34151
|
-
}
|
|
34152
|
-
const LEASE_OUTCOMES = /* @__PURE__ */ new Set([
|
|
34153
|
-
"claimed",
|
|
34154
|
-
"taken-over",
|
|
34155
|
-
"already-held",
|
|
34156
|
-
"renewed",
|
|
34157
|
-
"already-released",
|
|
34158
|
-
"released"
|
|
34159
|
-
]);
|
|
34160
|
-
const RELEASED_OUTCOMES = /* @__PURE__ */ new Set(["released", "already-released"]);
|
|
34161
|
-
const HELD_OUTCOMES = /* @__PURE__ */ new Set([
|
|
34162
|
-
"claimed",
|
|
34163
|
-
"taken-over",
|
|
34164
|
-
"renewed",
|
|
34165
|
-
"already-held"
|
|
34166
|
-
]);
|
|
34167
|
-
function nonNegativeInteger(value2) {
|
|
34168
|
-
return typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0;
|
|
34169
|
-
}
|
|
34170
|
-
function isLeaseResponse(value2) {
|
|
34171
|
-
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return false;
|
|
34172
|
-
const body = value2;
|
|
34173
|
-
if (typeof body.held !== "boolean") return false;
|
|
34174
|
-
if (typeof body.outcome !== "string" || !LEASE_OUTCOMES.has(body.outcome)) return false;
|
|
34175
|
-
if (!nonNegativeInteger(body.ownerEpoch) || !nonNegativeInteger(body.version)) return false;
|
|
34176
|
-
if (!nonNegativeInteger(body.serverNow) || !nonNegativeInteger(body.leaseUntil)) return false;
|
|
34177
|
-
if (body.releasedAt !== null && !nonNegativeInteger(body.releasedAt)) return false;
|
|
34178
|
-
if (body.held && body.releasedAt !== null) return false;
|
|
34179
|
-
if (body.held && RELEASED_OUTCOMES.has(body.outcome)) return false;
|
|
34180
|
-
if (!body.held && HELD_OUTCOMES.has(body.outcome)) return false;
|
|
34181
|
-
return true;
|
|
34182
|
-
}
|
|
34183
|
-
function resolveChannelLease(input) {
|
|
34184
|
-
if (!isLeaseResponse(input.body)) return { kind: "unknown" };
|
|
34185
|
-
const body = input.body;
|
|
34186
|
-
if (!body.held) {
|
|
34187
|
-
return { kind: "lost", reason: body.releasedAt === null ? "conflict" : "released" };
|
|
34188
|
-
}
|
|
34189
|
-
const remaining = body.leaseUntil - body.serverNow;
|
|
34190
|
-
if (!Number.isFinite(remaining) || remaining <= 0) return { kind: "unknown" };
|
|
34191
|
-
return {
|
|
34192
|
-
kind: "held",
|
|
34193
|
-
hold: {
|
|
34194
|
-
ownerId: input.ownerId,
|
|
34195
|
-
ownerEpoch: body.ownerEpoch,
|
|
34196
|
-
version: body.version,
|
|
34197
|
-
deadlineMono: input.requestStartedMono + remaining
|
|
34198
|
-
}
|
|
34199
|
-
};
|
|
34200
|
-
}
|
|
34201
|
-
function channelLeaseStillHeld(hold, nowMono, options = {}) {
|
|
34202
|
-
if (!hold || options.suspectedSuspend === true) return false;
|
|
34203
|
-
return nowMono < hold.deadlineMono;
|
|
34204
|
-
}
|
|
34205
|
-
function channelLeaseAnswerIsCurrent(input) {
|
|
34206
|
-
return !input.stopped && input.attempt === input.currentAttempt;
|
|
34207
|
-
}
|
|
34208
33887
|
const EXTENSION_ARTIFACT_ACTION_OPERATIONS = ["publishPublic"];
|
|
34209
33888
|
const EXTENSION_ARTIFACT_SOURCE_TYPES = ["project-file", "personal-chat-file"];
|
|
34210
|
-
const ID_PATTERN$
|
|
33889
|
+
const ID_PATTERN$3 = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
|
|
34211
33890
|
const LOCALE_PATTERN$1 = /^[a-z]{2,3}(?:-[A-Z]{2})?$/;
|
|
34212
33891
|
const EXTENSION_PATTERN = /^[a-z0-9]+$/;
|
|
34213
33892
|
const MAX_ACTIONS = 32;
|
|
@@ -34283,7 +33962,7 @@ function parseExtensionArtifactActions(value2, path = "manifest.contributes.arti
|
|
|
34283
33962
|
const action = record$3(raw, actionPath);
|
|
34284
33963
|
exactKeys$2(action, ["id", "title", "operation", "when"], ["localizations"], actionPath);
|
|
34285
33964
|
const parsedId = text$2(action.id, `${actionPath}.id`);
|
|
34286
|
-
if (!ID_PATTERN$
|
|
33965
|
+
if (!ID_PATTERN$3.test(parsedId) || parsedId.includes("..")) fail$6(`${actionPath}.id`, "invalid stable identifier");
|
|
34287
33966
|
if (!EXTENSION_ARTIFACT_ACTION_OPERATIONS.includes(action.operation)) {
|
|
34288
33967
|
fail$6(`${actionPath}.operation`, "unsupported operation");
|
|
34289
33968
|
}
|
|
@@ -34618,7 +34297,7 @@ function parseOptions(value2, path, expected) {
|
|
|
34618
34297
|
}));
|
|
34619
34298
|
}
|
|
34620
34299
|
const MAX_CHANNEL_CONTRIBUTIONS = CHANNEL_PROVIDERS.length;
|
|
34621
|
-
const ID_PATTERN$
|
|
34300
|
+
const ID_PATTERN$2 = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
|
|
34622
34301
|
const ENTRY_KEYS = /* @__PURE__ */ new Set(["id", "provider", "commands"]);
|
|
34623
34302
|
function fail$3(path, detail) {
|
|
34624
34303
|
throw new Error(`${path}: ${detail}`);
|
|
@@ -34636,7 +34315,7 @@ function parseExtensionChannelContributions(value2) {
|
|
|
34636
34315
|
const extra = Object.keys(source2).find((key) => !ENTRY_KEYS.has(key));
|
|
34637
34316
|
if (extra !== void 0) fail$3(path, `unknown field ${extra}`);
|
|
34638
34317
|
const id2 = source2.id;
|
|
34639
|
-
if (typeof id2 !== "string" || !ID_PATTERN$
|
|
34318
|
+
if (typeof id2 !== "string" || !ID_PATTERN$2.test(id2) || id2.includes("..")) {
|
|
34640
34319
|
fail$3(`${path}.id`, "invalid identifier");
|
|
34641
34320
|
}
|
|
34642
34321
|
const provider2 = source2.provider;
|
|
@@ -34702,8 +34381,8 @@ class UnsupportedExtensionApiVersionError extends Error {
|
|
|
34702
34381
|
}
|
|
34703
34382
|
name = "UnsupportedExtensionApiVersionError";
|
|
34704
34383
|
}
|
|
34705
|
-
const ID_PATTERN = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
|
|
34706
|
-
const SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
34384
|
+
const ID_PATTERN$1 = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
|
|
34385
|
+
const SEMVER_PATTERN$1 = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
34707
34386
|
const ENGINE_RANGE_PATTERN = /^(?:\^|~|>=|<=|>|<)?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
34708
34387
|
const ENTRYPOINT_PATTERN = /\.(?:cjs|mjs|js)$/;
|
|
34709
34388
|
const LOCALE_PATTERN = /^[a-z]{2,3}(?:-[A-Z]{2})?$/;
|
|
@@ -34792,7 +34471,7 @@ function string(value2, path) {
|
|
|
34792
34471
|
}
|
|
34793
34472
|
function id$1(value2, path) {
|
|
34794
34473
|
const parsed2 = string(value2, path);
|
|
34795
|
-
if (!ID_PATTERN.test(parsed2) || parsed2.includes("..")) fail$2(path, "invalid stable identifier");
|
|
34474
|
+
if (!ID_PATTERN$1.test(parsed2) || parsed2.includes("..")) fail$2(path, "invalid stable identifier");
|
|
34796
34475
|
return parsed2;
|
|
34797
34476
|
}
|
|
34798
34477
|
function relativePath(value2, path, requireScript = false) {
|
|
@@ -34968,7 +34647,7 @@ function parseExtensionManifest(value2, options) {
|
|
|
34968
34647
|
assertOnlyKeys(manifest2, TOP_LEVEL_KEYS, "manifest");
|
|
34969
34648
|
const parsedId = id$1(manifest2.id, "manifest.id");
|
|
34970
34649
|
const version2 = string(manifest2.version, "manifest.version");
|
|
34971
|
-
if (!SEMVER_PATTERN.test(version2)) fail$2("manifest.version", "expected semantic version");
|
|
34650
|
+
if (!SEMVER_PATTERN$1.test(version2)) fail$2("manifest.version", "expected semantic version");
|
|
34972
34651
|
const apiVersion = parseApiVersion(manifest2.apiVersion, options);
|
|
34973
34652
|
const channelApiVersion = parseChannelApiVersion(manifest2.channelApiVersion, apiVersion);
|
|
34974
34653
|
const engines = record$2(manifest2.engines, "manifest.engines");
|
|
@@ -35066,6 +34745,645 @@ function parseExtensionManifest(value2, options) {
|
|
|
35066
34745
|
contributes
|
|
35067
34746
|
};
|
|
35068
34747
|
}
|
|
34748
|
+
const ID_PATTERN = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
|
|
34749
|
+
const SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
34750
|
+
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
34751
|
+
const BASE64_PATTERN = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
34752
|
+
function isNewerVersion(candidate, installed) {
|
|
34753
|
+
if (!SEMVER_PATTERN.test(installed)) return false;
|
|
34754
|
+
const left = candidate.split(".").map(Number);
|
|
34755
|
+
const right = installed.split(".").map(Number);
|
|
34756
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
34757
|
+
if (left[index] !== right[index]) return left[index] > right[index];
|
|
34758
|
+
}
|
|
34759
|
+
return false;
|
|
34760
|
+
}
|
|
34761
|
+
function descriptor(value2) {
|
|
34762
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("official extension descriptor is invalid");
|
|
34763
|
+
const item = value2;
|
|
34764
|
+
if (Object.keys(item).sort().join(",") !== "archive,id,releaseUrl,sha256,version" || typeof item.id !== "string" || !ID_PATTERN.test(item.id) || item.id.includes("..") || typeof item.version !== "string" || !SEMVER_PATTERN.test(item.version) || typeof item.releaseUrl !== "string" || !item.releaseUrl.startsWith("https://github.com/buzzni/saycode-extensions/releases/tag/") || typeof item.archive !== "string" || basename(item.archive) !== item.archive || typeof item.sha256 !== "string" || !SHA256_PATTERN.test(item.sha256)) throw new Error("official extension descriptor is invalid");
|
|
34765
|
+
return item;
|
|
34766
|
+
}
|
|
34767
|
+
function catalog(value2) {
|
|
34768
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("official extension catalog is invalid");
|
|
34769
|
+
const record2 = value2;
|
|
34770
|
+
if (Object.keys(record2).sort().join(",") !== "extensions,schemaVersion" || record2.schemaVersion !== 1 || !Array.isArray(record2.extensions)) throw new Error("official extension catalog is invalid");
|
|
34771
|
+
const extensions = record2.extensions.map(descriptor);
|
|
34772
|
+
if (new Set(extensions.map((item) => item.id)).size !== extensions.length) {
|
|
34773
|
+
throw new Error("official extension catalog contains duplicate ids");
|
|
34774
|
+
}
|
|
34775
|
+
return { schemaVersion: 1, extensions };
|
|
34776
|
+
}
|
|
34777
|
+
class OfficialExtensionInstaller {
|
|
34778
|
+
constructor(options) {
|
|
34779
|
+
this.options = options;
|
|
34780
|
+
}
|
|
34781
|
+
/**
|
|
34782
|
+
* What the user could install right now (specs/desktop-official-extension-install-list R1).
|
|
34783
|
+
*
|
|
34784
|
+
* `install(id)` has always taken any catalog id; nothing could ask which ids exist, so the only
|
|
34785
|
+
* way to offer one was a button written by hand per extension. This is that missing half.
|
|
34786
|
+
*
|
|
34787
|
+
* Already-installed ids are left out: the list is an offer, not an inventory — the installed
|
|
34788
|
+
* ones are the rest of the panel.
|
|
34789
|
+
*/
|
|
34790
|
+
async installable() {
|
|
34791
|
+
let bundled = [];
|
|
34792
|
+
try {
|
|
34793
|
+
bundled = (await this.readCatalog()).extensions;
|
|
34794
|
+
} catch {
|
|
34795
|
+
bundled = [];
|
|
34796
|
+
}
|
|
34797
|
+
const versions = /* @__PURE__ */ new Map();
|
|
34798
|
+
for (const entry of bundled) versions.set(entry.id, entry.version);
|
|
34799
|
+
for (const entry of this.options.remote?.descriptors() ?? []) versions.set(entry.id, entry.version);
|
|
34800
|
+
return [...versions].filter(([id2, version2]) => !this.options.registry.get(id2) && !this.options.revocations?.isRevoked(id2, version2)).map(([id2, version2]) => ({ id: id2, version: version2 }));
|
|
34801
|
+
}
|
|
34802
|
+
async install(id2) {
|
|
34803
|
+
if (this.options.registry.get(id2)) throw new Error(`extension is already installed: ${id2}`);
|
|
34804
|
+
const source2 = await this.resolveArtifact(id2);
|
|
34805
|
+
const installed = await this.options.store.install(source2.archive);
|
|
34806
|
+
if (installed.id !== source2.metadata.id || installed.version !== source2.metadata.version) {
|
|
34807
|
+
await this.options.store.removeVersion(installed.id, installed.version);
|
|
34808
|
+
throw new Error("official extension artifact does not match its provenance");
|
|
34809
|
+
}
|
|
34810
|
+
try {
|
|
34811
|
+
await this.options.registry.apply(installed.id, { type: "installed", version: installed.version });
|
|
34812
|
+
} catch (error) {
|
|
34813
|
+
await this.options.store.removeVersion(installed.id, installed.version);
|
|
34814
|
+
throw error;
|
|
34815
|
+
}
|
|
34816
|
+
return { id: installed.id, version: installed.version, releaseUrl: source2.metadata.releaseUrl };
|
|
34817
|
+
}
|
|
34818
|
+
async reconcileBundledCompatibility() {
|
|
34819
|
+
const installedEntries = this.options.registry.entries();
|
|
34820
|
+
if (installedEntries.length === 0) return;
|
|
34821
|
+
const bundled = (await this.readCatalog()).extensions;
|
|
34822
|
+
let firstError = null;
|
|
34823
|
+
for (const { id: id2, state } of installedEntries) {
|
|
34824
|
+
try {
|
|
34825
|
+
const metadata = bundled.find((item) => item.id === id2);
|
|
34826
|
+
if (!metadata || !isNewerVersion(metadata.version, state.installedVersion)) continue;
|
|
34827
|
+
try {
|
|
34828
|
+
await this.options.store.readInstalledManifest(id2, state.installedVersion);
|
|
34829
|
+
continue;
|
|
34830
|
+
} catch (error) {
|
|
34831
|
+
if (!(error instanceof UnsupportedExtensionApiVersionError) || typeof error.apiVersion !== "number" || error.apiVersion >= error.minimumSupportedApiVersion) throw error;
|
|
34832
|
+
}
|
|
34833
|
+
const archive = await this.readArchive(metadata);
|
|
34834
|
+
await this.options.store.removeVersion(id2, metadata.version);
|
|
34835
|
+
const migrated = await this.options.store.install(archive);
|
|
34836
|
+
if (migrated.id !== id2 || migrated.version !== metadata.version) {
|
|
34837
|
+
await this.options.store.removeVersion(migrated.id, migrated.version);
|
|
34838
|
+
throw new Error("bundled compatibility artifact does not match its provenance");
|
|
34839
|
+
}
|
|
34840
|
+
try {
|
|
34841
|
+
await this.options.registry.apply(id2, {
|
|
34842
|
+
type: "compatibility-update-installed",
|
|
34843
|
+
version: migrated.version
|
|
34844
|
+
});
|
|
34845
|
+
} catch (error) {
|
|
34846
|
+
await this.options.store.removeVersion(migrated.id, migrated.version);
|
|
34847
|
+
throw error;
|
|
34848
|
+
}
|
|
34849
|
+
await this.options.store.removeVersion(id2, state.installedVersion);
|
|
34850
|
+
} catch (error) {
|
|
34851
|
+
firstError ??= error;
|
|
34852
|
+
}
|
|
34853
|
+
}
|
|
34854
|
+
if (firstError) throw firstError;
|
|
34855
|
+
}
|
|
34856
|
+
/** Ids the official channel owns — bundled or signed-remote. A development build must never
|
|
34857
|
+
* be able to stand in for one (ADR-050). */
|
|
34858
|
+
async isOfficialId(id2) {
|
|
34859
|
+
if (this.options.remote?.descriptorFor(id2)) return true;
|
|
34860
|
+
try {
|
|
34861
|
+
return (await this.readCatalog()).extensions.some((item) => item.id === id2);
|
|
34862
|
+
} catch {
|
|
34863
|
+
return true;
|
|
34864
|
+
}
|
|
34865
|
+
}
|
|
34866
|
+
async resolveArtifact(id2) {
|
|
34867
|
+
const remote = this.options.remote?.descriptorFor(id2) ?? null;
|
|
34868
|
+
if (remote) {
|
|
34869
|
+
return { archive: await this.options.remote.downloadArchive(remote), metadata: remote };
|
|
34870
|
+
}
|
|
34871
|
+
const metadata = (await this.readCatalog()).extensions.find((item) => item.id === id2);
|
|
34872
|
+
if (!metadata) throw new Error(`official extension is unavailable: ${id2}`);
|
|
34873
|
+
return { archive: await this.readArchive(metadata), metadata };
|
|
34874
|
+
}
|
|
34875
|
+
async readCatalog() {
|
|
34876
|
+
try {
|
|
34877
|
+
return catalog(JSON.parse(await readFile(join(this.options.resourceRoot, "catalog.v1.json"), "utf8")));
|
|
34878
|
+
} catch (error) {
|
|
34879
|
+
throw new Error(`official extension catalog could not be loaded: ${error instanceof Error ? error.message : String(error)}`);
|
|
34880
|
+
}
|
|
34881
|
+
}
|
|
34882
|
+
async readArchive(metadata) {
|
|
34883
|
+
const encoded = (await readFile(join(this.options.resourceRoot, metadata.archive), "utf8")).trim();
|
|
34884
|
+
if (!BASE64_PATTERN.test(encoded) || encoded.length % 4 !== 0) {
|
|
34885
|
+
throw new Error("official extension archive encoding is invalid");
|
|
34886
|
+
}
|
|
34887
|
+
const archive = Buffer.from(encoded, "base64");
|
|
34888
|
+
const checksum2 = createHash("sha256").update(archive).digest("hex");
|
|
34889
|
+
if (checksum2 !== metadata.sha256) throw new Error("official extension checksum mismatch");
|
|
34890
|
+
return archive;
|
|
34891
|
+
}
|
|
34892
|
+
}
|
|
34893
|
+
const CHANNEL_ADAPTER_IDS = {
|
|
34894
|
+
telegram: "buzzni.telegram",
|
|
34895
|
+
slack: "buzzni.slack",
|
|
34896
|
+
discord: "buzzni.discord"
|
|
34897
|
+
};
|
|
34898
|
+
const ADAPTER_IDS = new Set(Object.values(CHANNEL_ADAPTER_IDS));
|
|
34899
|
+
function createHostOfficialAdapterSource(input) {
|
|
34900
|
+
const installer = new OfficialExtensionInstaller({
|
|
34901
|
+
resourceRoot: input.resourceRoot,
|
|
34902
|
+
store: null,
|
|
34903
|
+
registry: null,
|
|
34904
|
+
...input.remote ? { remote: input.remote } : {}
|
|
34905
|
+
});
|
|
34906
|
+
const resolve2 = (id2) => {
|
|
34907
|
+
if (!ADAPTER_IDS.has(id2)) return Promise.reject(new Error(`not a messenger adapter: ${id2}`));
|
|
34908
|
+
return installer.resolveArtifact(id2);
|
|
34909
|
+
};
|
|
34910
|
+
return {
|
|
34911
|
+
versionOf: (id2) => resolve2(id2).then((artifact) => artifact.metadata.version, () => null),
|
|
34912
|
+
archive: async (id2) => (await resolve2(id2)).archive
|
|
34913
|
+
};
|
|
34914
|
+
}
|
|
34915
|
+
async function offeredChannelProviders(source2) {
|
|
34916
|
+
const offered = [];
|
|
34917
|
+
for (const [provider2, id2] of Object.entries(CHANNEL_ADAPTER_IDS)) {
|
|
34918
|
+
if (await source2.versionOf(id2)) offered.push(provider2);
|
|
34919
|
+
}
|
|
34920
|
+
return offered;
|
|
34921
|
+
}
|
|
34922
|
+
function describe(secretKey) {
|
|
34923
|
+
const { publicKey } = nacl.box.keyPair.fromSecretKey(secretKey);
|
|
34924
|
+
const digest = createHash("sha256").update(publicKey).digest("hex").slice(0, 16).toUpperCase();
|
|
34925
|
+
return {
|
|
34926
|
+
publicKey,
|
|
34927
|
+
secretKey,
|
|
34928
|
+
publicKeyBase64: Buffer.from(publicKey).toString("base64"),
|
|
34929
|
+
fingerprint: digest.match(/.{4}/g).join("-")
|
|
34930
|
+
};
|
|
34931
|
+
}
|
|
34932
|
+
async function openChannelHostKey(input) {
|
|
34933
|
+
if (!input.safeStorage.isEncryptionAvailable()) return null;
|
|
34934
|
+
const path = join(input.dir, "host-key.v1.json");
|
|
34935
|
+
let text2 = null;
|
|
34936
|
+
try {
|
|
34937
|
+
text2 = await readFile(path, "utf8");
|
|
34938
|
+
} catch (error) {
|
|
34939
|
+
if (error.code !== "ENOENT") throw error;
|
|
34940
|
+
}
|
|
34941
|
+
if (text2 !== null) {
|
|
34942
|
+
const parsed2 = JSON.parse(text2);
|
|
34943
|
+
if (parsed2.v !== 1 || typeof parsed2.sealed !== "string") throw new Error("channel host key file is unreadable");
|
|
34944
|
+
const secret = Buffer.from(input.safeStorage.decryptString(Buffer.from(parsed2.sealed, "base64")), "base64");
|
|
34945
|
+
if (secret.length !== nacl.box.secretKeyLength) throw new Error("channel host key file is unreadable");
|
|
34946
|
+
return describe(new Uint8Array(secret));
|
|
34947
|
+
}
|
|
34948
|
+
const { secretKey } = nacl.box.keyPair();
|
|
34949
|
+
await mkdir(input.dir, { recursive: true, mode: 448 });
|
|
34950
|
+
const sealed2 = input.safeStorage.encryptString(Buffer.from(secretKey).toString("base64")).toString("base64");
|
|
34951
|
+
const staging = `${path}.${process.pid}.tmp`;
|
|
34952
|
+
await writeFile(staging, JSON.stringify({ v: 1, sealed: sealed2 }), { mode: 384 });
|
|
34953
|
+
await rename(staging, path);
|
|
34954
|
+
return describe(secretKey);
|
|
34955
|
+
}
|
|
34956
|
+
function defaultIsAlive(pid) {
|
|
34957
|
+
try {
|
|
34958
|
+
process.kill(pid, 0);
|
|
34959
|
+
return true;
|
|
34960
|
+
} catch (error) {
|
|
34961
|
+
return error.code === "EPERM";
|
|
34962
|
+
}
|
|
34963
|
+
}
|
|
34964
|
+
async function acquireChannelHostLock(input) {
|
|
34965
|
+
const path = join(input.dir, "host.lock");
|
|
34966
|
+
const isAlive = input.isAlive ?? defaultIsAlive;
|
|
34967
|
+
for (let attempt2 = 0; attempt2 < 2; attempt2 += 1) {
|
|
34968
|
+
try {
|
|
34969
|
+
const handle = await open(path, "wx", 384);
|
|
34970
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, startedAt: Date.now() }));
|
|
34971
|
+
await handle.close();
|
|
34972
|
+
return {
|
|
34973
|
+
kind: "held",
|
|
34974
|
+
release: async () => {
|
|
34975
|
+
const current = await readFile(path, "utf8").then((text2) => JSON.parse(text2), () => null);
|
|
34976
|
+
if (current?.pid === process.pid) await rm(path, { force: true });
|
|
34977
|
+
}
|
|
34978
|
+
};
|
|
34979
|
+
} catch (error) {
|
|
34980
|
+
if (error.code !== "EEXIST") throw error;
|
|
34981
|
+
}
|
|
34982
|
+
let holder = null;
|
|
34983
|
+
try {
|
|
34984
|
+
const parsed2 = JSON.parse(await readFile(path, "utf8"));
|
|
34985
|
+
holder = typeof parsed2.pid === "number" && Number.isSafeInteger(parsed2.pid) ? parsed2.pid : null;
|
|
34986
|
+
} catch {
|
|
34987
|
+
holder = null;
|
|
34988
|
+
}
|
|
34989
|
+
if (holder === null || isAlive(holder)) return { kind: "refused" };
|
|
34990
|
+
await rm(path, { force: true });
|
|
34991
|
+
}
|
|
34992
|
+
return { kind: "refused" };
|
|
34993
|
+
}
|
|
34994
|
+
function isBytes(a) {
|
|
34995
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
|
|
34996
|
+
}
|
|
34997
|
+
function abytes(value2, length, title2 = "") {
|
|
34998
|
+
const bytes = isBytes(value2);
|
|
34999
|
+
const len = value2?.length;
|
|
35000
|
+
const needsLen = length !== void 0;
|
|
35001
|
+
if (!bytes || needsLen) {
|
|
35002
|
+
const prefix = title2 && `"${title2}" `;
|
|
35003
|
+
const ofLen = "";
|
|
35004
|
+
const got = bytes ? `length=${len}` : `type=${typeof value2}`;
|
|
35005
|
+
const message = prefix + "expected Uint8Array" + ofLen + ", got " + got;
|
|
35006
|
+
if (!bytes)
|
|
35007
|
+
throw new TypeError(message);
|
|
35008
|
+
throw new RangeError(message);
|
|
35009
|
+
}
|
|
35010
|
+
return value2;
|
|
35011
|
+
}
|
|
35012
|
+
function aexists(instance, checkFinished = true) {
|
|
35013
|
+
if (instance.destroyed)
|
|
35014
|
+
throw new Error("Hash instance has been destroyed");
|
|
35015
|
+
if (checkFinished && instance.finished)
|
|
35016
|
+
throw new Error("Hash#digest() has already been called");
|
|
35017
|
+
}
|
|
35018
|
+
function aoutput(out, instance) {
|
|
35019
|
+
abytes(out, void 0, "digestInto() output");
|
|
35020
|
+
const min = instance.outputLen;
|
|
35021
|
+
if (out.length < min) {
|
|
35022
|
+
throw new RangeError('"digestInto() output" expected to be of length >=' + min);
|
|
35023
|
+
}
|
|
35024
|
+
}
|
|
35025
|
+
function clean(...arrays) {
|
|
35026
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
35027
|
+
arrays[i].fill(0);
|
|
35028
|
+
}
|
|
35029
|
+
}
|
|
35030
|
+
function createView(arr) {
|
|
35031
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
35032
|
+
}
|
|
35033
|
+
function rotr(word, shift) {
|
|
35034
|
+
return word << 32 - shift | word >>> shift;
|
|
35035
|
+
}
|
|
35036
|
+
const hasHexBuiltin = /* @__PURE__ */ (() => (
|
|
35037
|
+
// @ts-ignore
|
|
35038
|
+
typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function"
|
|
35039
|
+
))();
|
|
35040
|
+
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
35041
|
+
function bytesToHex(bytes) {
|
|
35042
|
+
abytes(bytes);
|
|
35043
|
+
if (hasHexBuiltin)
|
|
35044
|
+
return bytes.toHex();
|
|
35045
|
+
let hex = "";
|
|
35046
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
35047
|
+
hex += hexes[bytes[i]];
|
|
35048
|
+
}
|
|
35049
|
+
return hex;
|
|
35050
|
+
}
|
|
35051
|
+
function createHasher(hashCons, info = {}) {
|
|
35052
|
+
const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
|
|
35053
|
+
const tmp = hashCons(void 0);
|
|
35054
|
+
hashC.outputLen = tmp.outputLen;
|
|
35055
|
+
hashC.blockLen = tmp.blockLen;
|
|
35056
|
+
hashC.canXOF = tmp.canXOF;
|
|
35057
|
+
hashC.create = (opts) => hashCons(opts);
|
|
35058
|
+
Object.assign(hashC, info);
|
|
35059
|
+
return Object.freeze(hashC);
|
|
35060
|
+
}
|
|
35061
|
+
const oidNist = (suffix) => ({
|
|
35062
|
+
// Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.
|
|
35063
|
+
// Larger suffix values would need base-128 OID encoding and a different length byte.
|
|
35064
|
+
oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
|
|
35065
|
+
});
|
|
35066
|
+
function Chi(a, b, c) {
|
|
35067
|
+
return a & b ^ ~a & c;
|
|
35068
|
+
}
|
|
35069
|
+
function Maj(a, b, c) {
|
|
35070
|
+
return a & b ^ a & c ^ b & c;
|
|
35071
|
+
}
|
|
35072
|
+
class HashMD {
|
|
35073
|
+
blockLen;
|
|
35074
|
+
outputLen;
|
|
35075
|
+
canXOF = false;
|
|
35076
|
+
padOffset;
|
|
35077
|
+
isLE;
|
|
35078
|
+
// For partial updates less than block size
|
|
35079
|
+
buffer;
|
|
35080
|
+
view;
|
|
35081
|
+
finished = false;
|
|
35082
|
+
length = 0;
|
|
35083
|
+
pos = 0;
|
|
35084
|
+
destroyed = false;
|
|
35085
|
+
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
35086
|
+
this.blockLen = blockLen;
|
|
35087
|
+
this.outputLen = outputLen;
|
|
35088
|
+
this.padOffset = padOffset;
|
|
35089
|
+
this.isLE = isLE;
|
|
35090
|
+
this.buffer = new Uint8Array(blockLen);
|
|
35091
|
+
this.view = createView(this.buffer);
|
|
35092
|
+
}
|
|
35093
|
+
update(data) {
|
|
35094
|
+
aexists(this);
|
|
35095
|
+
abytes(data);
|
|
35096
|
+
const { view, buffer, blockLen } = this;
|
|
35097
|
+
const len = data.length;
|
|
35098
|
+
for (let pos = 0; pos < len; ) {
|
|
35099
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
35100
|
+
if (take === blockLen) {
|
|
35101
|
+
const dataView = createView(data);
|
|
35102
|
+
for (; blockLen <= len - pos; pos += blockLen)
|
|
35103
|
+
this.process(dataView, pos);
|
|
35104
|
+
continue;
|
|
35105
|
+
}
|
|
35106
|
+
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|
35107
|
+
this.pos += take;
|
|
35108
|
+
pos += take;
|
|
35109
|
+
if (this.pos === blockLen) {
|
|
35110
|
+
this.process(view, 0);
|
|
35111
|
+
this.pos = 0;
|
|
35112
|
+
}
|
|
35113
|
+
}
|
|
35114
|
+
this.length += data.length;
|
|
35115
|
+
this.roundClean();
|
|
35116
|
+
return this;
|
|
35117
|
+
}
|
|
35118
|
+
digestInto(out) {
|
|
35119
|
+
aexists(this);
|
|
35120
|
+
aoutput(out, this);
|
|
35121
|
+
this.finished = true;
|
|
35122
|
+
const { buffer, view, blockLen, isLE } = this;
|
|
35123
|
+
let { pos } = this;
|
|
35124
|
+
buffer[pos++] = 128;
|
|
35125
|
+
clean(this.buffer.subarray(pos));
|
|
35126
|
+
if (this.padOffset > blockLen - pos) {
|
|
35127
|
+
this.process(view, 0);
|
|
35128
|
+
pos = 0;
|
|
35129
|
+
}
|
|
35130
|
+
for (let i = pos; i < blockLen; i++)
|
|
35131
|
+
buffer[i] = 0;
|
|
35132
|
+
view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);
|
|
35133
|
+
this.process(view, 0);
|
|
35134
|
+
const oview = createView(out);
|
|
35135
|
+
const len = this.outputLen;
|
|
35136
|
+
if (len % 4)
|
|
35137
|
+
throw new Error("_sha2: outputLen must be aligned to 32bit");
|
|
35138
|
+
const outLen = len / 4;
|
|
35139
|
+
const state = this.get();
|
|
35140
|
+
if (outLen > state.length)
|
|
35141
|
+
throw new Error("_sha2: outputLen bigger than state");
|
|
35142
|
+
for (let i = 0; i < outLen; i++)
|
|
35143
|
+
oview.setUint32(4 * i, state[i], isLE);
|
|
35144
|
+
}
|
|
35145
|
+
digest() {
|
|
35146
|
+
const { buffer, outputLen } = this;
|
|
35147
|
+
this.digestInto(buffer);
|
|
35148
|
+
const res = buffer.slice(0, outputLen);
|
|
35149
|
+
this.destroy();
|
|
35150
|
+
return res;
|
|
35151
|
+
}
|
|
35152
|
+
_cloneInto(to) {
|
|
35153
|
+
to ||= new this.constructor();
|
|
35154
|
+
to.set(...this.get());
|
|
35155
|
+
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
35156
|
+
to.destroyed = destroyed;
|
|
35157
|
+
to.finished = finished;
|
|
35158
|
+
to.length = length;
|
|
35159
|
+
to.pos = pos;
|
|
35160
|
+
if (length % blockLen)
|
|
35161
|
+
to.buffer.set(buffer);
|
|
35162
|
+
return to;
|
|
35163
|
+
}
|
|
35164
|
+
clone() {
|
|
35165
|
+
return this._cloneInto();
|
|
35166
|
+
}
|
|
35167
|
+
}
|
|
35168
|
+
const SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|
35169
|
+
1779033703,
|
|
35170
|
+
3144134277,
|
|
35171
|
+
1013904242,
|
|
35172
|
+
2773480762,
|
|
35173
|
+
1359893119,
|
|
35174
|
+
2600822924,
|
|
35175
|
+
528734635,
|
|
35176
|
+
1541459225
|
|
35177
|
+
]);
|
|
35178
|
+
const SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
35179
|
+
1116352408,
|
|
35180
|
+
1899447441,
|
|
35181
|
+
3049323471,
|
|
35182
|
+
3921009573,
|
|
35183
|
+
961987163,
|
|
35184
|
+
1508970993,
|
|
35185
|
+
2453635748,
|
|
35186
|
+
2870763221,
|
|
35187
|
+
3624381080,
|
|
35188
|
+
310598401,
|
|
35189
|
+
607225278,
|
|
35190
|
+
1426881987,
|
|
35191
|
+
1925078388,
|
|
35192
|
+
2162078206,
|
|
35193
|
+
2614888103,
|
|
35194
|
+
3248222580,
|
|
35195
|
+
3835390401,
|
|
35196
|
+
4022224774,
|
|
35197
|
+
264347078,
|
|
35198
|
+
604807628,
|
|
35199
|
+
770255983,
|
|
35200
|
+
1249150122,
|
|
35201
|
+
1555081692,
|
|
35202
|
+
1996064986,
|
|
35203
|
+
2554220882,
|
|
35204
|
+
2821834349,
|
|
35205
|
+
2952996808,
|
|
35206
|
+
3210313671,
|
|
35207
|
+
3336571891,
|
|
35208
|
+
3584528711,
|
|
35209
|
+
113926993,
|
|
35210
|
+
338241895,
|
|
35211
|
+
666307205,
|
|
35212
|
+
773529912,
|
|
35213
|
+
1294757372,
|
|
35214
|
+
1396182291,
|
|
35215
|
+
1695183700,
|
|
35216
|
+
1986661051,
|
|
35217
|
+
2177026350,
|
|
35218
|
+
2456956037,
|
|
35219
|
+
2730485921,
|
|
35220
|
+
2820302411,
|
|
35221
|
+
3259730800,
|
|
35222
|
+
3345764771,
|
|
35223
|
+
3516065817,
|
|
35224
|
+
3600352804,
|
|
35225
|
+
4094571909,
|
|
35226
|
+
275423344,
|
|
35227
|
+
430227734,
|
|
35228
|
+
506948616,
|
|
35229
|
+
659060556,
|
|
35230
|
+
883997877,
|
|
35231
|
+
958139571,
|
|
35232
|
+
1322822218,
|
|
35233
|
+
1537002063,
|
|
35234
|
+
1747873779,
|
|
35235
|
+
1955562222,
|
|
35236
|
+
2024104815,
|
|
35237
|
+
2227730452,
|
|
35238
|
+
2361852424,
|
|
35239
|
+
2428436474,
|
|
35240
|
+
2756734187,
|
|
35241
|
+
3204031479,
|
|
35242
|
+
3329325298
|
|
35243
|
+
]);
|
|
35244
|
+
const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
35245
|
+
class SHA2_32B extends HashMD {
|
|
35246
|
+
constructor(outputLen) {
|
|
35247
|
+
super(64, outputLen, 8, false);
|
|
35248
|
+
}
|
|
35249
|
+
get() {
|
|
35250
|
+
const { A, B, C, D, E, F, G, H } = this;
|
|
35251
|
+
return [A, B, C, D, E, F, G, H];
|
|
35252
|
+
}
|
|
35253
|
+
// prettier-ignore
|
|
35254
|
+
set(A, B, C, D, E, F, G, H) {
|
|
35255
|
+
this.A = A | 0;
|
|
35256
|
+
this.B = B | 0;
|
|
35257
|
+
this.C = C | 0;
|
|
35258
|
+
this.D = D | 0;
|
|
35259
|
+
this.E = E | 0;
|
|
35260
|
+
this.F = F | 0;
|
|
35261
|
+
this.G = G | 0;
|
|
35262
|
+
this.H = H | 0;
|
|
35263
|
+
}
|
|
35264
|
+
process(view, offset) {
|
|
35265
|
+
for (let i = 0; i < 16; i++, offset += 4)
|
|
35266
|
+
SHA256_W[i] = view.getUint32(offset, false);
|
|
35267
|
+
for (let i = 16; i < 64; i++) {
|
|
35268
|
+
const W15 = SHA256_W[i - 15];
|
|
35269
|
+
const W2 = SHA256_W[i - 2];
|
|
35270
|
+
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
|
|
35271
|
+
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
|
|
35272
|
+
SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
|
|
35273
|
+
}
|
|
35274
|
+
let { A, B, C, D, E, F, G, H } = this;
|
|
35275
|
+
for (let i = 0; i < 64; i++) {
|
|
35276
|
+
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
|
35277
|
+
const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
|
|
35278
|
+
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
|
|
35279
|
+
const T2 = sigma0 + Maj(A, B, C) | 0;
|
|
35280
|
+
H = G;
|
|
35281
|
+
G = F;
|
|
35282
|
+
F = E;
|
|
35283
|
+
E = D + T1 | 0;
|
|
35284
|
+
D = C;
|
|
35285
|
+
C = B;
|
|
35286
|
+
B = A;
|
|
35287
|
+
A = T1 + T2 | 0;
|
|
35288
|
+
}
|
|
35289
|
+
A = A + this.A | 0;
|
|
35290
|
+
B = B + this.B | 0;
|
|
35291
|
+
C = C + this.C | 0;
|
|
35292
|
+
D = D + this.D | 0;
|
|
35293
|
+
E = E + this.E | 0;
|
|
35294
|
+
F = F + this.F | 0;
|
|
35295
|
+
G = G + this.G | 0;
|
|
35296
|
+
H = H + this.H | 0;
|
|
35297
|
+
this.set(A, B, C, D, E, F, G, H);
|
|
35298
|
+
}
|
|
35299
|
+
roundClean() {
|
|
35300
|
+
clean(SHA256_W);
|
|
35301
|
+
}
|
|
35302
|
+
destroy() {
|
|
35303
|
+
this.destroyed = true;
|
|
35304
|
+
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|
35305
|
+
clean(this.buffer);
|
|
35306
|
+
}
|
|
35307
|
+
}
|
|
35308
|
+
class _SHA256 extends SHA2_32B {
|
|
35309
|
+
// We cannot use array here since array allows indexing by variable
|
|
35310
|
+
// which means optimizer/compiler cannot use registers.
|
|
35311
|
+
A = SHA256_IV[0] | 0;
|
|
35312
|
+
B = SHA256_IV[1] | 0;
|
|
35313
|
+
C = SHA256_IV[2] | 0;
|
|
35314
|
+
D = SHA256_IV[3] | 0;
|
|
35315
|
+
E = SHA256_IV[4] | 0;
|
|
35316
|
+
F = SHA256_IV[5] | 0;
|
|
35317
|
+
G = SHA256_IV[6] | 0;
|
|
35318
|
+
H = SHA256_IV[7] | 0;
|
|
35319
|
+
constructor() {
|
|
35320
|
+
super(32);
|
|
35321
|
+
}
|
|
35322
|
+
}
|
|
35323
|
+
const sha256 = /* @__PURE__ */ createHasher(
|
|
35324
|
+
() => new _SHA256(),
|
|
35325
|
+
/* @__PURE__ */ oidNist(1)
|
|
35326
|
+
);
|
|
35327
|
+
function channelLeaseSubject(input) {
|
|
35328
|
+
const material = JSON.stringify([input.provider, input.installationId]);
|
|
35329
|
+
return bytesToHex(sha256(new TextEncoder().encode(material)));
|
|
35330
|
+
}
|
|
35331
|
+
const LEASE_OUTCOMES = /* @__PURE__ */ new Set([
|
|
35332
|
+
"claimed",
|
|
35333
|
+
"taken-over",
|
|
35334
|
+
"already-held",
|
|
35335
|
+
"renewed",
|
|
35336
|
+
"already-released",
|
|
35337
|
+
"released"
|
|
35338
|
+
]);
|
|
35339
|
+
const RELEASED_OUTCOMES = /* @__PURE__ */ new Set(["released", "already-released"]);
|
|
35340
|
+
const HELD_OUTCOMES = /* @__PURE__ */ new Set([
|
|
35341
|
+
"claimed",
|
|
35342
|
+
"taken-over",
|
|
35343
|
+
"renewed",
|
|
35344
|
+
"already-held"
|
|
35345
|
+
]);
|
|
35346
|
+
function nonNegativeInteger(value2) {
|
|
35347
|
+
return typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0;
|
|
35348
|
+
}
|
|
35349
|
+
function isLeaseResponse(value2) {
|
|
35350
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return false;
|
|
35351
|
+
const body = value2;
|
|
35352
|
+
if (typeof body.held !== "boolean") return false;
|
|
35353
|
+
if (typeof body.outcome !== "string" || !LEASE_OUTCOMES.has(body.outcome)) return false;
|
|
35354
|
+
if (!nonNegativeInteger(body.ownerEpoch) || !nonNegativeInteger(body.version)) return false;
|
|
35355
|
+
if (!nonNegativeInteger(body.serverNow) || !nonNegativeInteger(body.leaseUntil)) return false;
|
|
35356
|
+
if (body.releasedAt !== null && !nonNegativeInteger(body.releasedAt)) return false;
|
|
35357
|
+
if (body.held && body.releasedAt !== null) return false;
|
|
35358
|
+
if (body.held && RELEASED_OUTCOMES.has(body.outcome)) return false;
|
|
35359
|
+
if (!body.held && HELD_OUTCOMES.has(body.outcome)) return false;
|
|
35360
|
+
return true;
|
|
35361
|
+
}
|
|
35362
|
+
function resolveChannelLease(input) {
|
|
35363
|
+
if (!isLeaseResponse(input.body)) return { kind: "unknown" };
|
|
35364
|
+
const body = input.body;
|
|
35365
|
+
if (!body.held) {
|
|
35366
|
+
return { kind: "lost", reason: body.releasedAt === null ? "conflict" : "released" };
|
|
35367
|
+
}
|
|
35368
|
+
const remaining = body.leaseUntil - body.serverNow;
|
|
35369
|
+
if (!Number.isFinite(remaining) || remaining <= 0) return { kind: "unknown" };
|
|
35370
|
+
return {
|
|
35371
|
+
kind: "held",
|
|
35372
|
+
hold: {
|
|
35373
|
+
ownerId: input.ownerId,
|
|
35374
|
+
ownerEpoch: body.ownerEpoch,
|
|
35375
|
+
version: body.version,
|
|
35376
|
+
deadlineMono: input.requestStartedMono + remaining
|
|
35377
|
+
}
|
|
35378
|
+
};
|
|
35379
|
+
}
|
|
35380
|
+
function channelLeaseStillHeld(hold, nowMono, options = {}) {
|
|
35381
|
+
if (!hold || options.suspectedSuspend === true) return false;
|
|
35382
|
+
return nowMono < hold.deadlineMono;
|
|
35383
|
+
}
|
|
35384
|
+
function channelLeaseAnswerIsCurrent(input) {
|
|
35385
|
+
return !input.stopped && input.attempt === input.currentAttempt;
|
|
35386
|
+
}
|
|
35069
35387
|
function syncSocketTokens(auth) {
|
|
35070
35388
|
const primary = auth.syncToken || auth.token;
|
|
35071
35389
|
if (!primary) return [];
|
|
@@ -46893,12 +47211,12 @@ class ChannelSettingsService {
|
|
|
46893
47211
|
const { principalKey } = this.requirePrincipal();
|
|
46894
47212
|
const epoch = this.options.accountEpoch();
|
|
46895
47213
|
const record2 = await this.records(principalKey);
|
|
46896
|
-
const
|
|
47214
|
+
const catalog2 = await this.readCatalog(record2);
|
|
46897
47215
|
const after = this.requirePrincipal();
|
|
46898
47216
|
if (after.principalKey !== principalKey || this.options.accountEpoch() !== epoch) {
|
|
46899
47217
|
throw new ChannelSettingsError("STALE_SNAPSHOT", "the signed-in account changed");
|
|
46900
47218
|
}
|
|
46901
|
-
return this.buildSnapshot(record2,
|
|
47219
|
+
return this.buildSnapshot(record2, catalog2);
|
|
46902
47220
|
}
|
|
46903
47221
|
async get(provider2) {
|
|
46904
47222
|
const snapshot = await this.snapshot();
|
|
@@ -47083,22 +47401,22 @@ class ChannelSettingsService {
|
|
|
47083
47401
|
});
|
|
47084
47402
|
await this.options.persistence.save(principalKey, next);
|
|
47085
47403
|
this.cache.set(principalKey, next);
|
|
47086
|
-
const
|
|
47087
|
-
const snapshot = this.buildSnapshot(next,
|
|
47404
|
+
const catalog2 = await this.readCatalog(next);
|
|
47405
|
+
const snapshot = this.buildSnapshot(next, catalog2);
|
|
47088
47406
|
this.options.onChanged(snapshot);
|
|
47089
47407
|
await this.options.onCommitted?.(next).catch(() => void 0);
|
|
47090
|
-
const settled = this.buildSnapshot(next,
|
|
47408
|
+
const settled = this.buildSnapshot(next, catalog2);
|
|
47091
47409
|
const before = snapshot.connections[connection.provider];
|
|
47092
47410
|
const after = settled.connections[connection.provider];
|
|
47093
47411
|
const botFields = ["botCheck", "botUsername", "botName", "botAppRef", "botScopeRef", "status"];
|
|
47094
47412
|
if (botFields.some((field) => after[field] !== before[field])) this.options.onChanged(settled);
|
|
47095
47413
|
return after;
|
|
47096
47414
|
}
|
|
47097
|
-
buildSnapshot(record2,
|
|
47415
|
+
buildSnapshot(record2, catalog2) {
|
|
47098
47416
|
const { principalKey } = this.requirePrincipal();
|
|
47099
47417
|
return buildChannelSettingsSnapshot({
|
|
47100
47418
|
record: record2,
|
|
47101
|
-
catalog,
|
|
47419
|
+
catalog: catalog2,
|
|
47102
47420
|
accountScopeKey: principalKey,
|
|
47103
47421
|
epoch: this.options.accountEpoch(),
|
|
47104
47422
|
installed: this.options.installedAdapters(),
|
|
@@ -59934,7 +60252,6 @@ const runKeychainCommand = (command, args, stdin) => new Promise((resolve2, reje
|
|
|
59934
60252
|
}, reject2);
|
|
59935
60253
|
});
|
|
59936
60254
|
const TICK_MS = 3e4;
|
|
59937
|
-
const PROVIDERS = ["telegram", "slack", "discord"];
|
|
59938
60255
|
function report(code) {
|
|
59939
60256
|
process.stderr.write(`[channel-host] ${code}
|
|
59940
60257
|
`);
|
|
@@ -59988,8 +60305,9 @@ async function runChannelHost() {
|
|
|
59988
60305
|
if (!layout) report("CHANNEL_HOST_EXTENSIONS_DIR_DENIED");
|
|
59989
60306
|
const sandbox = layout ? await loadAdapterSandbox({ resolveFrom: import.meta.url, deniedReads: layout.deniedReads }) : null;
|
|
59990
60307
|
const bootstrapPath = join(dirname(fileURLToPath(import.meta.url)), "channel-extension-host.mjs");
|
|
60308
|
+
const officialRoot = join(dirname(fileURLToPath(import.meta.url)), "official-extensions");
|
|
59991
60309
|
let opened = null;
|
|
59992
|
-
let
|
|
60310
|
+
let catalog2 = null;
|
|
59993
60311
|
const store = createHostStateStore({ dir: init.dataDir, safeStorage });
|
|
59994
60312
|
const controller = createHostController({
|
|
59995
60313
|
machineId: init.machine.id,
|
|
@@ -59999,7 +60317,7 @@ async function runChannelHost() {
|
|
|
59999
60317
|
serverFor: (binding, token) => createHostServerClient({ apiBaseUrl: binding.apiBaseUrl, token }),
|
|
60000
60318
|
openRuntime: async ({ binding, token, isPaused }) => {
|
|
60001
60319
|
opened = await openHostRuntime({ init, binding, token, isPaused, safeStorage, bridge, sandbox, extensionRoot: layout?.extensionRoot ?? init.extensionsDir, bootstrapPath, report });
|
|
60002
|
-
|
|
60320
|
+
catalog2 = createRemoteExtensionCatalog({
|
|
60003
60321
|
cachePath: join(init.dataDir, "extension-catalog.v1.json"),
|
|
60004
60322
|
revocations: opened.extensions.revocations,
|
|
60005
60323
|
fetch: (url2) => fetch(url2, { signal: AbortSignal.timeout(3e4) }),
|
|
@@ -60008,14 +60326,16 @@ async function runChannelHost() {
|
|
|
60008
60326
|
return opened;
|
|
60009
60327
|
},
|
|
60010
60328
|
reconcileAdapters: async (specs) => {
|
|
60011
|
-
if (!opened
|
|
60012
|
-
|
|
60013
|
-
|
|
60329
|
+
if (!opened) throw Object.assign(new Error("NOT_READY"), { code: "CHANNEL_HOST_NOT_READY" });
|
|
60330
|
+
if (catalog2) {
|
|
60331
|
+
await catalog2.load();
|
|
60332
|
+
await catalog2.refresh().catch(() => report("CHANNEL_HOST_CATALOG_REFRESH_FAILED"));
|
|
60333
|
+
}
|
|
60014
60334
|
const { registry: registry2, store: extensionStore, revocations } = opened.extensions;
|
|
60015
60335
|
return reconcileHostExtensions(specs, {
|
|
60016
60336
|
registry: registry2,
|
|
60017
60337
|
store: extensionStore,
|
|
60018
|
-
|
|
60338
|
+
official: createHostOfficialAdapterSource({ resourceRoot: officialRoot, remote: catalog2 }),
|
|
60019
60339
|
isRevoked: (id2, version2) => revocations.isRevoked(id2, version2)
|
|
60020
60340
|
});
|
|
60021
60341
|
},
|
|
@@ -60027,22 +60347,15 @@ async function runChannelHost() {
|
|
|
60027
60347
|
void controller.handleSettings(wire).then((answer) => bridge.send({ t: "settings-result", id: id2, wire: answer })).catch(() => bridge.send({ t: "settings-result", id: id2, wire: { sealed: "", error: "HOST_CALL_FAILED" } }));
|
|
60028
60348
|
});
|
|
60029
60349
|
await controller.start().catch(() => report("CHANNEL_HOST_RESUME_FAILED"));
|
|
60030
|
-
const
|
|
60031
|
-
cachePath: join(init.dataDir, "extension-catalog.probe.json"),
|
|
60032
|
-
revocations: { record: async () => void 0 },
|
|
60033
|
-
fetch: async () => {
|
|
60034
|
-
throw new Error("probe");
|
|
60035
|
-
},
|
|
60036
|
-
onDiagnostic: () => void 0
|
|
60037
|
-
}) !== null;
|
|
60350
|
+
const offered = await offeredChannelProviders(createHostOfficialAdapterSource({ resourceRoot: officialRoot, remote: null }));
|
|
60038
60351
|
bridge.send({
|
|
60039
60352
|
t: "ready",
|
|
60040
60353
|
hostKey: key.publicKeyBase64,
|
|
60041
60354
|
fingerprint: key.fingerprint,
|
|
60042
60355
|
custody: "available",
|
|
60043
60356
|
isolation: sandbox ? "available" : "unavailable",
|
|
60044
|
-
// Without isolation
|
|
60045
|
-
providers: sandbox
|
|
60357
|
+
// Without isolation no adapter may run here, so nothing is offered.
|
|
60358
|
+
providers: sandbox ? offered : []
|
|
60046
60359
|
});
|
|
60047
60360
|
const tick = setInterval(() => {
|
|
60048
60361
|
void controller.tick().catch(() => report("CHANNEL_HOST_TICK_FAILED"));
|