@buzzni/saycode-cli 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,10 @@ import { createInterface } from "node:readline";
3
3
  import { readFile } from "node:fs/promises";
4
4
  import { resolve, sep } from "node:path";
5
5
  import { pathToFileURL } from "node:url";
6
+ const ASSET_PERMISSIONS = ["assets.read", "assets.write", "files.select", "drafts.prepare"];
7
+ function usesAssetWorkflow(permissions) {
8
+ return permissions.some((permission) => ASSET_PERMISSIONS.includes(permission));
9
+ }
6
10
  const EXTENSION_ARTIFACT_ACTION_OPERATIONS = ["publishPublic"];
7
11
  const EXTENSION_ARTIFACT_SOURCE_TYPES = ["project-file", "personal-chat-file"];
8
12
  const ID_PATTERN$2 = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
@@ -225,6 +229,10 @@ function parseExtensionChannelContributions(value) {
225
229
  });
226
230
  }
227
231
  const EXTENSION_PERMISSIONS = [
232
+ "assets.read",
233
+ "assets.write",
234
+ "files.select",
235
+ "drafts.prepare",
228
236
  "projects.read",
229
237
  "remoteFiles.read",
230
238
  "remoteFiles.write",
@@ -254,7 +262,7 @@ const EXTENSION_PERMISSIONS = [
254
262
  const SAYCODE_EXTENSION_API_VERSION = 3;
255
263
  const SAYCODE_EXTENSION_MINIMUM_API_VERSION = 2;
256
264
  const MAX_EXTENSION_API_WINDOW_WIDTH = 2;
257
- const SAYCODE_EXTENSION_ENGINE_VERSION = "1.0.0";
265
+ const SAYCODE_EXTENSION_ENGINE_VERSION = "1.1.0";
258
266
  const SAYCODE_CHANNEL_API_VERSION = CHANNEL_API_VERSION;
259
267
  const SAYCODE_CHANNEL_MINIMUM_API_VERSION = CHANNEL_MINIMUM_API_VERSION;
260
268
  class UnsupportedChannelApiVersionError extends Error {
@@ -284,7 +292,7 @@ const V2_PERMISSIONS = /* @__PURE__ */ new Set([
284
292
  "browserViewer.install",
285
293
  "browserViewer.installChrome"
286
294
  ]);
287
- const V3_PERMISSIONS = /* @__PURE__ */ new Set(["artifacts.publishPublic"]);
295
+ const V3_PERMISSIONS = /* @__PURE__ */ new Set(["artifacts.publishPublic", "assets.read", "assets.write", "files.select", "drafts.prepare"]);
288
296
  const CHANNEL_PERMISSIONS = /* @__PURE__ */ new Set([
289
297
  "channels.receive",
290
298
  "channels.ack",
@@ -1284,6 +1292,7 @@ async function startExtensionHost(deps) {
1284
1292
  });
1285
1293
  const runtime = new ExtensionHostRuntime({
1286
1294
  manifest,
1295
+ requestTimeoutMs: usesAssetWorkflow(manifest.permissions) ? 13e4 : void 0,
1287
1296
  loadModule: (entrypoint) => deps.importModule(protocolUrl(deps.extensionId, entrypoint)),
1288
1297
  send: (message) => deps.bridge.send(message)
1289
1298
  });
package/index.mjs CHANGED
@@ -23,6 +23,7 @@ import { execFile, spawn } from "node:child_process";
23
23
  import { randomUUID, createPublicKey, verify, createHash, randomBytes } from "node:crypto";
24
24
  import { fileURLToPath } from "node:url";
25
25
  import { createRequire } from "node:module";
26
+ import net from "node:net";
26
27
  import { Transform } from "node:stream";
27
28
  import { pipeline } from "node:stream/promises";
28
29
  import { promisify } from "node:util";
@@ -45,7 +46,7 @@ function _mergeNamespaces(n, m) {
45
46
  }
46
47
  return Object.freeze(Object.defineProperty(n, Symbol.toStringTag, { value: "Module" }));
47
48
  }
48
- const version$2 = "0.7.1";
49
+ const version$2 = "0.7.3";
49
50
  const manifest = {
50
51
  version: version$2
51
52
  };
@@ -270,8 +271,8 @@ function getCurrentLanguage() {
270
271
  }
271
272
  const workKo = {
272
273
  "work.homeTitle": "어떤 작업을 도와드릴까요?",
273
- "work.new": "새 워크",
274
- "work.archived": "보관된 대화",
274
+ "work.new": "새 chat 시작",
275
+ "work.archived": "보관된 chat",
275
276
  "work.active": "활성 대화로 돌아가기",
276
277
  "work.docx": "문서",
277
278
  "work.pdf": "PDF 문서",
@@ -305,6 +306,85 @@ const workKo = {
305
306
  "work.xlsxDescription": "표와 수식 작성 및 편집",
306
307
  "work.pptxDescription": "발표자료 작성 및 편집"
307
308
  };
309
+ const setupFailureHintKo = {
310
+ "setupHint.file-locked": "다른 프로그램이 교체할 파일을 사용 중입니다. 열려 있는 터미널과 Claude·Codex 창을 닫고 다시 시도하세요. 설치된 도구와 로그인은 유지됩니다.",
311
+ "setupHint.npm-missing": "npm을 찾지 못했습니다. Node.js LTS(npm 포함)를 설치한 뒤 다시 시도하세요. Saycode를 다시 시작할 필요는 없습니다.",
312
+ "setupHint.network": "네트워크 또는 프록시 때문에 내려받지 못했습니다. 연결을 확인하고 다시 시도하세요. 회사 프록시를 쓴다면 npm 프록시 설정이 필요할 수 있습니다.",
313
+ "setupHint.permission": "설치 폴더에 쓸 권한이 없습니다. Saycode를 관리자 권한이 아닌 일반 사용자로 실행하고, npm 전역 설치 폴더가 사용자 폴더 안에 있는지 확인하세요.",
314
+ "setupHint.openLogs": "로그 폴더 열기",
315
+ "setupHint.gitMissing": "Git을 찾지 못했습니다. 프로젝트와 작업 사본에 Git이 필요합니다. 설치한 뒤 다시 확인하세요. 다시 시작할 필요는 없습니다.",
316
+ "setupHint.recheck": "다시 확인",
317
+ "setupHint.gitGuide": "Git 설치"
318
+ };
319
+ const usageEntryKo = {
320
+ ...setupFailureHintKo,
321
+ "usageChoice.title": "어떻게 사용하시겠어요?",
322
+ "usageChoice.personal": "개인 사용",
323
+ "usageChoice.personalBody": "나만의 프로젝트를 AI와 함께 만들고 관리하세요.",
324
+ "usageChoice.organization": "조직 사용",
325
+ "usageChoice.organizationBody": "팀원과 프로젝트를 공유하고 함께 개발하세요.",
326
+ "onboarding.page.machine": "이 컴퓨터 등록",
327
+ "registration.registered": "등록됨",
328
+ "registration.required": "등록 필요",
329
+ "registration.checking": "확인 중…",
330
+ "registration.registering": "등록 중…",
331
+ "registration.title": "이 컴퓨터 등록",
332
+ "registration.body": "조직에서 사용할 전용 머신으로 이 컴퓨터를 연결하세요.",
333
+ "registration.finish": "조직 작업 시작",
334
+ "registration.organization": "조직",
335
+ "registration.computer": "컴퓨터",
336
+ "registration.thisComputer": "이 컴퓨터",
337
+ "registration.chooseOrganization": "등록할 조직을 선택하세요.",
338
+ "registration.noOrganization": "가입한 조직이 없어요. 조직에 가입한 계정으로 로그인하거나 조직 관리에서 가입 상태를 확인하세요.",
339
+ "registration.selectFailed": "조직을 전환하지 못했어요. 다시 선택해 주세요.",
340
+ "registration.action": "이 컴퓨터 등록",
341
+ "machineSettings.registerThisComputer": "내 컴퓨터 등록",
342
+ "machineSettings.registerMachine": "머신 등록",
343
+ "registration.linked": "이 컴퓨터가 조직에 연결됐어요.",
344
+ "registration.identityPending": "이 컴퓨터의 조직·전용 머신 유형·사용자 배정을 확인 중입니다. 기존 연결은 자동으로 변경하지 않습니다.",
345
+ "localTools.title": "추가 CLI 도구",
346
+ "localTools.body": "Codex와 Claude의 멀티 계정 도구를 함께 준비합니다.",
347
+ "localTools.unavailable": "이 버전에서 로컬 도구 설치를 사용할 수 없어요.",
348
+ "localTools.checking": "확인 중",
349
+ "localTools.missing": "설치 필요",
350
+ "localTools.installing": "설치 중…",
351
+ "localTools.installed": "설치됨",
352
+ "localTools.failed": "설치 실패",
353
+ "localTools.prerequisite": "사전 준비 필요",
354
+ "localTools.prerequisiteBody": "{command}를 설치하고 다시 시도하세요. Saycode를 다시 시작할 필요는 없습니다.",
355
+ "localTools.installGuide": "설치 안내",
356
+ "localTools.retry": "미완료 도구 다시 설치",
357
+ "localTools.install": "추가 도구 설치",
358
+ "localTools.required": "추가 도구 준비 필요",
359
+ "onboarding.checklist": "온보딩 체크리스트",
360
+ "onboarding.checklistBody": "작업에 필요한 도구를 준비하세요. 선택한 단계부터 나머지 설치를 자동으로 진행합니다.",
361
+ "onboarding.autoStart": "이 단계부터 자동 진행",
362
+ "onboarding.autoStop": "자동 진행 중지",
363
+ "onboarding.autoRetry": "자동 진행 다시 시도",
364
+ "onboarding.autoFailed": "자동 진행을 멈췄어요. {detail}",
365
+ "onboarding.autoNeedsAction": "아래 상태를 확인하고 필요한 준비 후 다시 시도하세요.",
366
+ "onboarding.autoWait.organization": "등록할 조직을 선택하면 이어서 진행합니다.",
367
+ "onboarding.autoWait.identity": "이 컴퓨터의 조직·유형·사용자 연결 확인을 기다리고 있습니다.",
368
+ "onboarding.autoWait.machine": "먼저 이 컴퓨터 등록 단계에서 연결을 완료하세요.",
369
+ "onboarding.autoWait.login": "로그인한 뒤 등록 완료를 누르거나, 개인 계정 없이 진행하려면 등록 안 함을 선택하세요.",
370
+ "onboarding.autoWait.platform": "이 플랫폼의 AI 인증 확인은 아직 지원되지 않습니다.",
371
+ "onboarding.autoWait.verification": "설치 후 AI 도구의 실행 상태를 다시 확인하고 있습니다.",
372
+ "onboarding.autoWait.cli": "먼저 Saycode CLI 단계에서 도구 준비를 완료하세요.",
373
+ "onboarding.autoWorking": "{step} 준비 중…"
374
+ };
375
+ const homeTabsKo = {
376
+ "homeTabs.greeting": "{name}님, 안녕하세요",
377
+ "homeTabs.label": "홈 모드",
378
+ "homeTabs.project": "Build",
379
+ "homeTabs.work": "Chat",
380
+ "homeTabs.develop": "Develop",
381
+ "homeTabs.new": "새 기획으로 시작",
382
+ "homeTabs.quick": "새 프로젝트 시작",
383
+ "homeTabs.git": "코드 저장소 가져오기",
384
+ "homeTabs.zip": "ZIP/파일 가져오기",
385
+ "homeTabs.folder": "머신 폴더 사용하기",
386
+ "homeTabs.categories": "프로젝트 범주"
387
+ };
308
388
  const projectRelocationKO = {
309
389
  "projectRelocation.notFound": "이 머신에서 프로젝트 폴더를 확인하지 못했습니다: {path}. 프로젝트를 이 머신으로 복사하거나 프로젝트 설정에서 실제 폴더 경로를 지정하세요.",
310
390
  "projectRelocation.ambiguous": "일치하는 프로젝트 폴더가 여러 개입니다: {paths}. 프로젝트 설정에서 사용할 폴더를 지정하세요.",
@@ -312,9 +392,89 @@ const projectRelocationKO = {
312
392
  "sessionCreation.dirNotFound": "원격 머신에 디렉토리가 없습니다: {directory}",
313
393
  "sessionCreation.workspaceNotSet": "이 머신의 워크스페이스 경로가 설정되지 않았습니다. 머신 워크스페이스를 먼저 설정해주세요."
314
394
  };
395
+ const sessionShareKo = {
396
+ "sessionShare.menuAction": "조직에 공유",
397
+ "sessionShare.dialogTitle": "대화 세션 공유",
398
+ "sessionShare.dialogDescription": "내가 속한 조직에 이 대화의 읽기 전용 스냅샷을 공유합니다.",
399
+ "sessionShare.readOnlyScope": "이 대화와 본문에서 참조한 파일의 공유 시점 사본만 공유되며, 다른 프로젝트 파일과 대화는 공개되지 않습니다.",
400
+ "sessionShare.addOrganization": "공유할 조직",
401
+ "sessionShare.selectOrganization": "조직 선택",
402
+ "sessionShare.noOrganizations": "추가로 공유할 수 있는 소속 조직이 없습니다.",
403
+ "sessionShare.currentOrganizations": "현재 공유 중인 조직",
404
+ "sessionShare.noGrants": "아직 공유된 조직이 없습니다.",
405
+ "sessionShare.revokeOrganization": "{organization} 공유 해제",
406
+ "sessionShare.shareAction": "공유",
407
+ "sessionShare.sharedSuccess": "대화가 {organization} 조직에 공유되었습니다",
408
+ "sessionShare.revokedSuccess": "대화의 조직 공유가 해제되었습니다",
409
+ "sessionShare.copyFailed": "공유 링크를 복사하지 못했습니다",
410
+ "sessionShare.copySuccess": "대화 공유 링크가 복사되었습니다",
411
+ "sessionShare.copyLink": "공유 링크 복사",
412
+ "sessionShare.messengerShareOrganization": "{organization} 메신저로 공유",
413
+ "sessionShare.notFound": "공유된 대화를 열 수 없습니다",
414
+ "sessionShare.notFoundDescription": "공유가 해제되었거나 현재 계정이 대상 조직의 활성 멤버가 아닙니다.",
415
+ "sessionShare.loginRequired": "로그인이 필요합니다",
416
+ "sessionShare.loginDescription": "공유 대상 조직의 Saycode 계정으로 로그인해 주세요.",
417
+ "sessionShare.continueLogin": "로그인하고 계속",
418
+ "sessionShare.switchAccount": "다른 계정으로 로그인",
419
+ "sessionShare.loading": "공유된 대화를 불러오는 중…",
420
+ "sessionShare.readOnly": "읽기 전용",
421
+ "sessionShare.fileUnavailable": "공유되지 않은 파일",
422
+ "sessionShare.workspaceUnavailable": "작업공간 정보를 확인한 뒤 다시 공유해 주세요.",
423
+ "sessionShare.tooLong": "공유할 대화가 너무 깁니다.",
424
+ "sessionShare.invalidRequest": "대화 공유 요청이 올바르지 않습니다.",
425
+ "sessionShare.targetMembershipRequired": "소속된 조직에만 공유할 수 있습니다.",
426
+ "sessionShare.requestFailed": "대화 공유 요청을 처리하지 못했습니다.",
427
+ "sessionShare.shareNotFound": "이 대화의 공유 정보를 찾을 수 없습니다.",
428
+ "sessionShare.publicSection": "링크가 있는 누구나",
429
+ "sessionShare.publicToggle": "로그인 없이 링크로 열람 허용",
430
+ "sessionShare.publicDescription": "켜면 링크를 가진 누구나 로그인 없이 이 대화의 공유 시점 사본을 볼 수 있습니다.",
431
+ "sessionShare.publicWarning": "이 링크를 받은 사람은 계정 없이 대화 전문과 참조된 파일을 볼 수 있습니다. 민감한 내용이 없는지 확인해 주세요.",
432
+ "sessionShare.publicLinkLabel": "공개 링크",
433
+ "sessionShare.publicCopyLink": "공개 링크 복사",
434
+ "sessionShare.publicCopySuccess": "공개 링크가 복사되었습니다",
435
+ "sessionShare.publicEnabledSuccess": "대화가 링크 공개로 전환되었습니다",
436
+ "sessionShare.publicDisabledSuccess": "대화의 링크 공개가 해제되었습니다",
437
+ "sessionShare.publicRevokeHint": "공개를 해제하면 이 링크는 영구히 만료되고, 다시 켜면 새 링크가 발급됩니다.",
438
+ "sessionShare.publicNotFoundDescription": "공개가 해제되었거나 링크가 만료되었습니다.",
439
+ "sessionShare.publicRateLimited": "조회 요청이 너무 많습니다. 잠시 후 다시 열어 주세요.",
440
+ "sessionShare.publicUnavailableTitle": "지금은 대화를 열 수 없습니다",
441
+ "sessionShare.publicUnavailable": "지금은 공유된 대화를 불러올 수 없습니다. 잠시 후 다시 시도해 주세요.",
442
+ "sessionShare.rateLimited": "공유 요청이 너무 잦습니다. 잠시 후 다시 시도해 주세요.",
443
+ "sessionShare.snapshotRefreshScope": "공유하거나 공개를 켤 때마다, 이미 공유된 대상이 보는 사본도 지금 대화 내용으로 함께 갱신됩니다.",
444
+ "sessionShare.publicLiveWhileSharing": "공개 링크가 켜져 있습니다. 지금 조직에 공유하면 링크를 가진 사람이 보는 내용도 지금 대화로 갱신됩니다.",
445
+ "sessionShare.grantsLiveWhilePublishing": "이미 {count}개 조직에 공유 중입니다. 공개를 켜면 해당 조직이 보는 사본도 지금 대화로 갱신됩니다."
446
+ };
447
+ const workAreaKo = {
448
+ "workArea.addPanel": "작업 패널 추가",
449
+ "workArea.diff": "변경",
450
+ "workArea.files": "파일",
451
+ "workArea.terminal": "터미널",
452
+ "workArea.browser": "브라우저",
453
+ "workArea.tools": "작업 도구",
454
+ "workArea.hideTools": "도구 접기",
455
+ "workArea.showTools": "도구 펼치기",
456
+ "workArea.resize": "작업 도구 너비 조절",
457
+ "workArea.changedFiles": "변경된 파일",
458
+ "workArea.noChanges": "현재 작업 폴더에서 변경된 파일이 없습니다.",
459
+ "workArea.selectProject": "파일을 보려면 프로젝트를 선택하세요.",
460
+ "workArea.openFile": "파일 열기",
461
+ "workArea.selectFile": "오른쪽 파일 트리에서 파일을 선택하세요.",
462
+ "workArea.newTerminal": "새 터미널",
463
+ "workArea.noTerminal": "터미널을 열어 현재 작업 폴더에서 명령을 실행하세요.",
464
+ "workArea.filterFiles": "파일 필터…",
465
+ "workArea.viewSource": "소스 보기",
466
+ "workArea.preview": "미리보기",
467
+ "workArea.expandFolders": "폴더 펼치기",
468
+ "workArea.collapseFolders": "폴더 접기",
469
+ "workArea.contentSearch": "내용 검색",
470
+ "workArea.splitDiff": "좌우 비교",
471
+ "workArea.unifiedDiff": "한 줄 비교"
472
+ };
315
473
  const redesignKo = {
316
- "ux2.pinnedConversations": "핀된 대화",
317
- "ux2.pinnedProjects": "핀된 프로젝트",
474
+ ...sessionShareKo,
475
+ ...workAreaKo,
476
+ "ux2.pinnedConversations": "고정된 대화",
477
+ "ux2.pinnedProjects": "고정된 프로젝트",
318
478
  "ux2.selectOrganization": "조직 선택",
319
479
  "ux2.selectMachine": "머신 선택",
320
480
  "ux2.expandTools": "펼치기",
@@ -328,6 +488,8 @@ const redesignKo = {
328
488
  "ux2.remainingUsage": "AI 잔여량",
329
489
  "ux2.projectViewOptions": "프로젝트 보기 옵션",
330
490
  "ux2.projectTools": "프로젝트 도구",
491
+ "ux2.pinConversation": "대화 고정",
492
+ "ux2.unpinConversation": "대화 고정 해제",
331
493
  "ux2.sharingExtensions": "공유·메모리 확장",
332
494
  "ux2.conversationFiles": "대화·파일 메뉴",
333
495
  "ux2.allProjects": "전체",
@@ -340,14 +502,10 @@ const redesignKo = {
340
502
  "ux2.setupDetails": "{step} · 설정 방법",
341
503
  "ux2.work": "대화",
342
504
  "ux2.workHint": "프로젝트 전체의 대화와 터미널",
343
- "ux2.createHint": "프로젝트를 어떻게 시작할까요?",
344
505
  "ux2.blank": "새 프로젝트",
345
- "ux2.template": "템플릿에서 시작",
346
506
  "ux2.git": "Git 저장소",
347
507
  "ux2.zip": "ZIP·파일",
348
508
  "ux2.folder": "기존 폴더",
349
- "ux2.machine": "실행 환경",
350
- "ux2.machineHint": "프로젝트와 에이전트가 실행될 머신입니다.",
351
509
  "ux2.addMachine": "머신 추가",
352
510
  "ux2.name": "프로젝트 이름",
353
511
  "ux2.choose": "폴더 선택",
@@ -445,10 +603,10 @@ const machineResourceMetricsKo = {
445
603
  "machineResourceMetrics.unavailable": "측정 불가 — 연결을 확인해 주세요"
446
604
  };
447
605
  const hubSessionKo = {
448
- "hubSession.enable": "이 대화를 프로젝트 허브로",
606
+ "hubSession.enable": "프로젝트 허브로 설정",
449
607
  "hubSession.disable": "프로젝트 허브 해제",
450
608
  "hubSession.hint": "같은 프로젝트의 다른 대화가 턴을 끝내거나 입력을 기다리면 이 대화에 메시지로 알려줍니다",
451
- "hubSession.newSessionLabel": "이 대화를 프로젝트 허브로 시작",
609
+ "hubSession.newSessionLabel": "프로젝트 허브로 설정",
452
610
  "hubSession.description": "프로젝트의 작업 세션들을 관리할 수 있습니다. 프로젝트 당 허브는 1개만 설정이 가능합니다.",
453
611
  "hubSession.badge": "프로젝트 허브",
454
612
  "hubSession.newSessionHint": "다른 대화의 완료·질문을 여기로 모읍니다",
@@ -1206,7 +1364,10 @@ const githubTriggersKo = {
1206
1364
  "githubTriggers.validationRequired": "이름과 템플릿을 입력하세요."
1207
1365
  };
1208
1366
  const memoryHubKo = {
1209
- "memoryHub.button": "메모리",
1367
+ "memoryHub.button": "프로젝트 메모리",
1368
+ "memoryHub.manageButton": "프로젝트 메모리 관리",
1369
+ "workspaceMenu.conversationProject": "대화 및 프로젝트",
1370
+ "workspaceMenu.statusResources": "상태 및 리소스",
1210
1371
  "memoryHub.ariaLabel": "메모리 허브",
1211
1372
  "memoryHub.title": "메모리 허브",
1212
1373
  "memoryHub.refresh": "새로고침",
@@ -1281,6 +1442,7 @@ const personalChatsKo = {
1281
1442
  "personalChats.title": "내 채팅",
1282
1443
  "personalChats.newChat": "새 채팅",
1283
1444
  "personalChats.searchPlaceholder": "채팅 검색",
1445
+ "personalChats.sortRecent": "최신 작업순으로 정렬",
1284
1446
  "personalChats.empty": "채팅이 없습니다",
1285
1447
  "personalChats.emptyHint": "프로젝트를 선택하지 않고 채팅을 시작하세요.",
1286
1448
  "personalChats.emptySearch": "검색과 일치하는 채팅이 없습니다.",
@@ -1643,6 +1805,7 @@ const sessionTitleKo = {
1643
1805
  "sessionTitle.saveFailed": "제목을 저장하지 못했습니다."
1644
1806
  };
1645
1807
  const accountSettingsKo = {
1808
+ "account.mySettings": "내 설정",
1646
1809
  "account.logoutConfirmTitle": "로그아웃하시겠습니까?",
1647
1810
  "account.logoutConfirmDescription": "{username} 계정에서 로그아웃합니다. 진행 중인 작업 화면은 닫힐 수 있습니다.",
1648
1811
  "settings.sectionsAria": "환경설정 섹션",
@@ -1727,6 +1890,10 @@ const projectScopeKo = {
1727
1890
  "projectScope.daemonUpgradeRequired": "Aplus CLI 업데이트 후 다시 시도해 주세요."
1728
1891
  };
1729
1892
  const machineSettingsConsoleKo = {
1893
+ "machineConsole.environmentStatus": "환경·상태",
1894
+ "machineConsole.homeDirectory": "홈 경로",
1895
+ "machineConsole.ownedMachines": "내가 등록한 머신",
1896
+ "machineConsole.otherMachines": "다른 머신",
1730
1897
  "machineConsole.online": "온라인",
1731
1898
  "machineConsole.offline": "오프라인",
1732
1899
  "machineConsole.personal": "개인",
@@ -1807,53 +1974,39 @@ const connectionIndicatorKo = {
1807
1974
  "connectionIndicator.restored": "연결 복구",
1808
1975
  "connectionIndicator.aria": "실시간 동기화 연결 상태: {state}"
1809
1976
  };
1810
- const voiceDictationKo = {
1811
- "voice.finishAndSend": "받아쓰고 전송",
1812
- "voice.finishAndSendAria": "녹음을 끝내고 받아쓴 내용을 바로 전송"
1813
- };
1814
1977
  const newSessionProfileKo = {
1815
1978
  "newSession.profile.label": "프로필",
1979
+ "newSession.profile.agentHeading": "사용할 AI",
1980
+ "newSession.profile.savedHeading": "AI 프로필",
1981
+ "newSession.profile.savedHint": "선택하면 AI·모델·작업 환경이 함께 바뀝니다",
1982
+ "newSession.profile.saveSetup": "현재 설정 저장",
1983
+ "newSession.profile.manageSetups": "AI 프로필 관리",
1816
1984
  "newSession.profile.selectAria": "실행 옵션 프로필",
1817
1985
  "newSession.profile.none": "프로필 없음",
1818
- "newSession.profile.save": "현재 조합 저장",
1819
- "newSession.profile.saveAria": "현재 실행 옵션을 프로필로 저장",
1820
1986
  "newSession.profile.executionSettings": "실행 설정",
1821
- "newSession.profile.saveExecutionSettings": "모델·에포트 설정 저장",
1987
+ "newSession.profile.editSettings": "편집",
1988
+ "newSession.profile.saveChanges": "변경사항 저장",
1989
+ "newSession.profile.instructionsField": "추가 지시문",
1990
+ "newSession.profile.badgeIconField": "표식 아이콘",
1991
+ "newSession.profile.badgeColorField": "표식 색상",
1822
1992
  "newSession.profile.worktreeOn": "작업 사본 사용",
1823
1993
  "newSession.profile.worktreeOff": "작업 사본 안 함",
1824
- "newSession.profile.manage": "프로필 관리",
1825
- "newSession.profile.nameLabel": "프로필 이름",
1826
- "newSession.profile.namePlaceholder": "예: 리뷰용",
1827
- "newSession.profile.confirmSave": "저장",
1828
1994
  "newSession.profile.cancel": "취소",
1829
1995
  "newSession.profile.saved": "프로필 설정이 변경되었습니다.",
1830
- "newSession.profile.dirty": "수정됨",
1831
- "newSession.profile.dirtyTitle": "프로필을 적용한 뒤 옵션을 직접 바꿨습니다.",
1832
1996
  "newSession.profile.saveFailed": "프로필을 저장하지 못했습니다.",
1833
- "newSession.profile.managerTitle": "프로필 관리",
1997
+ "newSession.profile.managerTitle": "AI 프로필 관리",
1834
1998
  "newSession.profile.managerEmpty": "저장된 프로필이 없습니다.",
1835
- "newSession.profile.rename": "이름 변경",
1836
1999
  "newSession.profile.delete": "삭제",
1837
2000
  "newSession.profile.close": "닫기",
1838
- "newSession.profile.renameAria": "{name} 이름 변경",
1839
- "newSession.profile.renameApplyAria": "{name} 이름 변경 적용",
1840
2001
  "newSession.profile.deleteAria": "{name} 삭제",
1841
2002
  "newSession.profile.fallbackAgent": "{value} 에이전트를 지금 쓸 수 없어 현재 선택을 유지했습니다.",
1842
2003
  "newSession.profile.fallbackModel": "{value} 모델이 목록에 없어 기본값으로 시작합니다.",
1843
2004
  "newSession.profile.fallbackEffort": "{value} 강도를 이 모델에서 쓸 수 없어 기본값으로 시작합니다.",
1844
2005
  "newSession.profile.fallbackWorktree": "이 프로젝트는 git 저장소가 아니라 작업 사본을 끕니다.",
1845
2006
  // specs/desktop-session-personas T9
1846
- // rename/renameAria 와 같은 규칙 — 가시 텍스트에 이름을 넣으면 버튼 폭이 행마다 달라진다.
1847
- "newSession.profile.editPersona": "페르소나 편집",
1848
- "newSession.profile.editPersonaAria": "{name} 페르소나 편집",
1849
- "newSession.profile.instructionsLabel": "{name} 지시문",
1850
2007
  "newSession.profile.instructionsPlaceholder": "예: 리뷰 관점에서만 답하라",
1851
2008
  "newSession.profile.instructionsTooLong": "지시문이 너무 깁니다 ({max}자 이하로 줄여주세요).",
1852
- "newSession.profile.instructionsSave": "{name} 지시문 저장",
1853
- "newSession.profile.skillsLabel": "배포할 스킬",
1854
- "newSession.profile.badgeIconLabel": "{name} 표식 아이콘",
1855
- "newSession.profile.badgeColorLabel": "{name} 표식 색상",
1856
- "newSession.profile.badgeSave": "{name} 표식 저장"
2009
+ "newSession.profile.skillsLabel": "배포할 스킬"
1857
2010
  };
1858
2011
  const agentBoardBroadcastKo = {
1859
2012
  "agentBoard.broadcastSelected": "선택됨",
@@ -1977,7 +2130,20 @@ const sidebarUpdateKo = {
1977
2130
  "sidebarUpdate.unsupported": "이 빌드에서는 자동 업데이트를 쓸 수 없습니다",
1978
2131
  "sidebarUpdate.currentVersion": "현재 v{version}",
1979
2132
  "sidebarUpdate.newVersion": "v{version}",
1980
- "sidebarUpdate.installFailed": "업데이트가 적용되지 않았습니다 — v{expected} 로 바뀌었어야 하는데 여전히 v{actual} 입니다. 다시 확인"
2133
+ "sidebarUpdate.installFailed": "업데이트가 적용되지 않았습니다 — v{expected} 로 바뀌었어야 하는데 여전히 v{actual} 입니다. 다시 확인",
2134
+ "sidebarUpdate.availableTitle": "업데이트 가능",
2135
+ "sidebarUpdate.downloadingTitle": "업데이트 다운로드 중",
2136
+ "sidebarUpdate.readyTitle": "업데이트 준비 완료",
2137
+ "sidebarUpdate.failedTitle": "업데이트 실패",
2138
+ "sidebarUpdate.completedTitle": "New Updated!",
2139
+ "sidebarUpdate.availableBody": "Saycode v{version} 업데이트를 사용할 수 있어요.",
2140
+ "sidebarUpdate.downloadingBody": "Saycode v{version} 다운로드 중입니다.",
2141
+ "sidebarUpdate.completedBody": "Saycode v{version} 업데이트가 완료됐어요.",
2142
+ "sidebarUpdate.installing": "업데이트 적용 중…",
2143
+ "sidebarUpdate.update": "업데이트",
2144
+ "sidebarUpdate.restartNotice": "재시작 전에 대화 상태를 저장합니다.",
2145
+ "sidebarUpdate.minimize": "최소화",
2146
+ "sidebarUpdate.releaseNotes": "Release notes"
1981
2147
  };
1982
2148
  const globalPauseKo = {
1983
2149
  "settings.globalPause.enable": "전역 일시정지 (새 자동 액션 차단)",
@@ -2075,21 +2241,24 @@ const composerAddMenuKo = {
2075
2241
  "shortcuts.action.composerAttachFileDescription": "포커스된 메시지 작성란의 파일 선택기를 엽니다."
2076
2242
  };
2077
2243
  const composerQuickCommandsKo = {
2078
- "composer.quickCommands.title": "자주 사용하는 명령어",
2079
- "composer.quickCommands.emptyDraftHint": "입력창에 메시지를 입력하신 후에 저장할 수 있습니다.",
2244
+ "composer.quickCommands.title": "자주 쓰는 프롬프트",
2245
+ "composer.quickCommands.description": "선택하면 입력창에 추가됩니다",
2246
+ "composer.quickCommands.emptyDraftHint": "입력창에 문장을 쓰면 저장할 수 있습니다.",
2080
2247
  "composer.quickCommands.trigger": "Quick Commands",
2081
- "composer.quickCommands.saveCurrent": "현재 입력을 이 프로젝트의 Quick Command로 저장",
2082
- "composer.quickCommands.saveCurrentGlobal": "현재 입력을 모든 프로젝트의 Quick Command로 저장",
2248
+ "composer.quickCommands.saveScope": "현재 입력 저장",
2249
+ "composer.quickCommands.saveCurrent": "이 프로젝트에 저장",
2250
+ "composer.quickCommands.saveCurrentGlobal": "모든 프로젝트에 저장",
2083
2251
  "composer.quickCommands.storageWriteFailed": "이 기기에 저장하지 못했습니다 — 앱을 다시 시작하면 목록이 사라질 수 있습니다.",
2084
- "composer.quickCommands.empty": "저장된 Quick Command가 없습니다.",
2252
+ "composer.quickCommands.empty": "아직 저장한 프롬프트가 없습니다.",
2085
2253
  "composer.quickCommands.projectScope": "이 프로젝트",
2086
2254
  "composer.quickCommands.globalScope": "모든 프로젝트",
2087
- "composer.quickCommands.remove": '"{label}" 삭제'
2255
+ "composer.quickCommands.remove": '"{label}" 삭제',
2256
+ "composer.quickCommands.rename": '"{label}" 이름 변경'
2088
2257
  };
2089
2258
  const promptHistoryKo = {
2090
2259
  "composer.promptHistory.trigger": "입력 기록",
2091
2260
  "composer.promptHistory.triggerMac": "입력 기록 (⌃R)",
2092
- "composer.promptHistory.search": "보낸 프롬프트 검색",
2261
+ "composer.promptHistory.search": "프롬프트 검색",
2093
2262
  "composer.promptHistory.empty": "아직 보낸 프롬프트가 없습니다.",
2094
2263
  "composer.promptHistory.noMatch": "일치하는 기록이 없습니다.",
2095
2264
  "composer.promptHistory.count": "{count}건 · 이 기기에만 저장",
@@ -2495,7 +2664,6 @@ const catalogExtensionsKo = {
2495
2664
  ...sessionTokenUsageKo,
2496
2665
  ...worktreeSetupKo,
2497
2666
  ...connectionIndicatorKo,
2498
- ...voiceDictationKo,
2499
2667
  ...newSessionProfileKo,
2500
2668
  ...agentBoardBroadcastKo,
2501
2669
  ...judgementEvidenceKo,
@@ -2538,11 +2706,21 @@ const catalogExtensionsKo = {
2538
2706
  ...agentBrowserConsoleKo
2539
2707
  };
2540
2708
  const onboardingKo = {
2709
+ "onboarding.accountPool.registering": "현재 로그인 계정 등록 중…",
2710
+ "onboarding.accountPool.ready": "현재 계정 등록·선택 완료",
2711
+ "onboarding.accountPool.failed": "계정 등록에 실패했습니다. 다른 AI가 준비되면 계속 진행할 수 있습니다.",
2712
+ "onboarding.accountPool.login-required": "Codex 인증을 갱신하려면 codex-multi-auth login으로 로그인한 뒤 다시 시도하세요.",
2713
+ "onboarding.accountPool.register": "등록 완료",
2714
+ "onboarding.accountPool.retry": "계정 등록 다시 시도",
2715
+ "onboarding.accountPool.usageGuide": "이 컴퓨터를 선택하세요. 좌측 하단 … → AI 잔여량 보이기를 켜면 중앙 패널 우측 상단에 표시됩니다. 눌러 등록된 계정을 확인하고 다른 계정으로 전환할 수 있습니다. 다른 머신을 선택하면 해당 머신의 계정이 표시됩니다.",
2716
+ "onboarding.accountPool.skip": "등록 안 함",
2717
+ "onboarding.accountPool.organizationHint": "개인 계정을 등록하지 않아도 조직에서 등록된 계정을 통해 사용할 수 있습니다.",
2541
2718
  "boot.stage.app": "앱 준비 중…",
2542
2719
  "boot.stage.localRuntime": "로컬 작업공간 준비 중…",
2543
2720
  "login.dialogTitle": "Saycode 계정으로 로그인",
2544
2721
  "login.dialogDescription": "로컬 데이터는 이 컴퓨터에 그대로 남고, 현재 창만 클라우드 작업공간으로 전환됩니다.",
2545
2722
  "login.dialogClose": "로그인 닫기",
2723
+ "login.loginDevServer": "개발 서버에 로그인",
2546
2724
  "guestRecovery.title": "로컬 작업공간을 준비하지 못했어요",
2547
2725
  "guestRecovery.body": "Saycode가 로컬 런타임을 시작하지 못했습니다. 다시 시도하거나 계정으로 로그인해 클라우드 작업공간을 사용하세요.",
2548
2726
  "guestRecovery.retry": "다시 시도",
@@ -2585,9 +2763,9 @@ const onboardingKo = {
2585
2763
  "onboarding.standalone.machineConnected": "로컬 머신 연결됨",
2586
2764
  "onboarding.standalone.machineWaiting": "로컬 머신 연결 대기",
2587
2765
  "onboarding.claude.title": "Claude Code 로그인",
2588
- "onboarding.claude.body": "터미널에서 Claude Code를 시작하고 /login을 선택해 브라우저 인증을 완료하세요.",
2766
+ "onboarding.claude.body": "터미널에서 claude auth login으로 로그인한 뒤 등록 완료를 누르세요.",
2589
2767
  "onboarding.codex.title": "Codex 로그인",
2590
- "onboarding.codex.body": "codex login은 브라우저를 열어 ChatGPT 로그인을 진행합니다. status 명령으로 완료를 확인할 수 있습니다.",
2768
+ "onboarding.codex.body": "터미널에서 codex login으로 로그인한 뒤 등록 완료를 누르세요.",
2591
2769
  "onboarding.firstSession.title": "첫 대화 시작",
2592
2770
  "onboarding.firstSession.body": "프로젝트를 선택하고 Claude Code 또는 Codex로 첫 요청을 보내세요.",
2593
2771
  "onboarding.firstSession.action": "첫 대화 만들기",
@@ -2603,7 +2781,7 @@ const onboardingKo = {
2603
2781
  "onboarding.page.happyTitle": "Saycode CLI 준비",
2604
2782
  "onboarding.page.happyBody": "설치와 버전을 확인하고, 필요한 작업만 이 화면에서 실행합니다.",
2605
2783
  "onboarding.page.agentsTitle": "Claude Code와 Codex",
2606
- "onboarding.page.agentsBody": "사용할 AI 도구를 설치하고 계정 로그인을 완료하세요.",
2784
+ "onboarding.page.agentsBody": "사용할 AI 도구를 준비하고 개인 계정 등록 여부를 선택하세요.",
2607
2785
  "onboarding.page.notificationsTitle": "알림 설정",
2608
2786
  "onboarding.page.notificationsBody": "응답 요청과 작업 완료를 놓치지 않도록 알림과 사운드를 설정하세요.",
2609
2787
  "onboarding.page.back": "이전",
@@ -2635,6 +2813,11 @@ const onboardingKo = {
2635
2813
  "onboarding.cli.checking": "설치 상태 확인 중…",
2636
2814
  "onboarding.cli.checkFailedTitle": "설치 상태를 확인하지 못했습니다",
2637
2815
  "onboarding.cli.checkFailedBody": "연결을 확인한 뒤 다시 시도해 주세요.",
2816
+ "onboarding.cli.checkFailed.check-failed": "업데이트 정보를 서버에서 받지 못했고, 이 컴퓨터의 실행 상태도 아직 확인되지 않았습니다. 잠시 후 다시 시도해 주세요.",
2817
+ "onboarding.cli.checkFailed.not-listed": "서버의 업데이트 목록에 이 컴퓨터가 없습니다. 이 컴퓨터 등록을 다시 확인해 주세요.",
2818
+ "onboarding.cli.checkFailed.offline": "이 컴퓨터의 Saycode가 오프라인입니다. 데몬이 실행 중인지 확인한 뒤 다시 시도해 주세요.",
2819
+ "onboarding.cli.checkFailed.version-unknown": "이 컴퓨터의 Saycode CLI 버전을 읽지 못했습니다. 잠시 후 다시 시도해 주세요.",
2820
+ "onboarding.cli.runningBody": "이 컴퓨터에서 Saycode CLI {version}이(가) 실행 중입니다. 최신 버전인지는 지금 확인하지 못했습니다.",
2638
2821
  "onboarding.cli.retry": "다시 확인",
2639
2822
  "onboarding.cli.nav.checking": "확인 중",
2640
2823
  "onboarding.cli.nav.install-required": "설치 필요",
@@ -2743,7 +2926,20 @@ const recoveryPasskeyKo = {
2743
2926
  "recoveryPasskey.openInBrowser": "웹 설정에서 열기"
2744
2927
  };
2745
2928
  const ko = {
2929
+ "assetWorkflow.open": "문서 양식",
2930
+ "assetWorkflow.unavailable": "선택 가능한 문서 양식 확장이 없습니다. 설정에서 설치하고 권한을 승인해 주세요.",
2931
+ "assetWorkflow.failed": "작업을 완료하지 못했습니다. 입력을 보존했습니다. 다시 열어 확인해 주세요.",
2932
+ "assetWorkflow.apply": "양식 적용",
2933
+ "assetWorkflow.save": "개인 양식 저장",
2934
+ "assetWorkflow.remove": "개인 양식 삭제",
2935
+ "assetWorkflow.cancel": "취소",
2936
+ "assetWorkflow.assetsRead": "개인 양식 조회 및 원본 읽기",
2937
+ "assetWorkflow.assetsWrite": "개인 양식 생성·수정·삭제",
2938
+ "assetWorkflow.filesSelect": "직접 선택한 파일 읽기",
2939
+ "assetWorkflow.draftsPrepare": "선택한 대화의 초안과 첨부 준비",
2746
2940
  ...workKo,
2941
+ ...usageEntryKo,
2942
+ ...homeTabsKo,
2747
2943
  "orgRouting.sharedInput": "이 입력은 조직의 지정 분류 머신에서 처리했습니다.",
2748
2944
  "orgRouting.title": "조직 공유 자동 모드",
2749
2945
  "orgRouting.ready": "분류 머신 준비됨",
@@ -2839,14 +3035,12 @@ const ko = {
2839
3035
  "happyCliUpdate.checkRetry": "다시 확인",
2840
3036
  "happyCliUpdate.openManager": "머신 보기",
2841
3037
  "happyCliUpdate.retry": "다시 시도",
3038
+ "happyCliUpdate.snoozeOneDay": "하루동안 안보기",
2842
3039
  "happyCliUpdate.dismiss": "닫기",
2843
3040
  "happyCliUpdate.onlineEmpty": "온라인 머신이 없습니다.",
2844
3041
  "happyCliUpdate.showMore": "{count}대 더보기",
2845
3042
  "happyCliUpdate.showLess": "접기",
2846
3043
  "happyCliUpdate.updateAll": "{count}대 모두 업데이트",
2847
- "happyCliUpdate.confirmAll": "{count}대 업데이트",
2848
- "happyCliUpdate.confirmAllNotice": "{count}대의 연결이 잠시 끊길 수 있습니다.",
2849
- "happyCliUpdate.cancel": "취소",
2850
3044
  // Boot screen
2851
3045
  "boot.preparing": "Saycode 준비 중...",
2852
3046
  "boot.stage.credentials": "로그인 상태 확인 중…",
@@ -2898,9 +3092,12 @@ const ko = {
2898
3092
  "account.syncing": "캐시된 워크스페이스를 최신 정보로 동기화하는 중입니다.",
2899
3093
  "sidebar.newProject": "새 프로젝트",
2900
3094
  "sidebar.modeChats": "대화",
2901
- "sidebar.modeProjects": "프로젝트",
3095
+ "sidebar.modeProjects": "Project",
2902
3096
  "sidebar.modeAria": "사이드바 보기",
2903
3097
  "sidebar.primaryNavAria": "주요 탐색",
3098
+ "sidebar.saycodeHome": "Saycode 홈",
3099
+ "sidebar.workBoard": "작업 보드",
3100
+ "sidebar.externalIntegrations": "외부 연동",
2904
3101
  "sidebar.more": "더보기",
2905
3102
  "sidebar.moreAria": "추가 탐색",
2906
3103
  "sidebar.allConversations": "전체 대화",
@@ -2922,7 +3119,6 @@ const ko = {
2922
3119
  "sidebarPanel.newChatAria": "{name} 프로젝트에서 새 대화 시작",
2923
3120
  "home.eyebrow": "워크스페이스 홈",
2924
3121
  "home.newProject": "새 프로젝트",
2925
- "home.searchActive": "‘{query}’ 검색 결과 · 프로젝트 {count}개",
2926
3122
  "detailTab.chat": "대화",
2927
3123
  "detailTab.sessions": "대화 목록",
2928
3124
  "chat.machineOnline": "머신 온라인",
@@ -2954,10 +3150,6 @@ const ko = {
2954
3150
  "newProject.nameLabel": "프로젝트 이름",
2955
3151
  "newProject.existingFolderLabel": "기존 폴더",
2956
3152
  "newProject.choose": "선택",
2957
- "newProject.summaryBlank": "빈 동기화 프로젝트를 만듭니다.",
2958
- "newProject.summaryGit": "프로젝트를 만들고 선택한 머신에서 Git 가져오기를 시작합니다.",
2959
- "newProject.summaryZip": "프로젝트를 만들고 선택한 머신에서 선택한 ZIP/파일을 가져옵니다.",
2960
- "newProject.summaryExistingFolder": "기존 절대 경로 폴더를 동기화 프로젝트로 등록합니다.",
2961
3153
  "newProject.creating": "만드는 중...",
2962
3154
  "newProject.createAction": "프로젝트 만들기",
2963
3155
  "newProject.importAction": "프로젝트 가져오기",
@@ -2983,8 +3175,6 @@ const ko = {
2983
3175
  "composer.attachmentPreviewOpen": "{name} 크게 보기",
2984
3176
  "composer.attachmentPreviewDialog": "첨부 이미지 미리보기",
2985
3177
  "composer.attachmentPreviewClose": "첨부 이미지 미리보기 닫기",
2986
- "composer.voiceLiveTitle": "실시간 음성 인식",
2987
- "composer.voiceLiveHint": "말씀하시면 인식된 문장이 여기에 바로 표시됩니다.",
2988
3178
  "upload.kindImage": "이미지",
2989
3179
  "upload.kindFile": "파일",
2990
3180
  "upload.tooLarge": '"{name}"이(가) 너무 큽니다. 최대 {limit}MB까지 첨부할 수 있습니다.',
@@ -4146,7 +4336,7 @@ const ko = {
4146
4336
  "login.mfaMethodsLead": "이 계정은 2단계 인증이 필요합니다. 패스키 또는 등록한 인증 앱으로 인증해주세요.",
4147
4337
  "login.mfaPasskey": "패스키로 인증",
4148
4338
  "login.mfaPasskeyRetry": "패스키 인증 다시 열기",
4149
- "login.mfaPasskeyWaiting": "시스템 브라우저에서 iPhone 또는 이 Mac의 패스키 인증을 완료해주세요.",
4339
+ "login.mfaPasskeyWaiting": "시스템 브라우저에서 휴대폰 또는 이 컴퓨터의 패스키 인증을 완료해주세요.",
4150
4340
  "login.mfaTotpFallback": "브라우저를 닫아도 인증 앱 코드로 계속할 수 있습니다.",
4151
4341
  "login.mfaCodeLabel": "인증 코드",
4152
4342
  "login.mfaCodePlaceholder": "6자리 숫자",
@@ -4155,6 +4345,8 @@ const ko = {
4155
4345
  "login.mfaInvalidCode": "인증 코드가 올바르지 않습니다. 다시 시도해주세요.",
4156
4346
  "login.mfaExpired": "인증 시간이 만료되었습니다. 다시 로그인해주세요.",
4157
4347
  "login.mfaRateLimited": "시도가 너무 많습니다. 잠시 후 다시 시도해주세요.",
4348
+ "login.mfaUnavailable": "인증 서버에서 로그인을 마치지 못했습니다({detail}). 잠시 후 비밀번호부터 다시 시도해주세요.",
4349
+ "login.mfaCodeReplayed": "이 코드는 이미 사용했습니다. 인증 앱에 새 코드가 나오면 입력해주세요.",
4158
4350
  "login.mfaUnsupported": "이 계정에서 사용할 수 있는 2단계 인증 방법이 없습니다. 웹에서 인증 앱 또는 패스키를 등록한 뒤 다시 시도해주세요.",
4159
4351
  "login.mfaPasskeyFailed": "패스키 인증을 시작하지 못했습니다. 다시 시도하거나 인증 앱 코드를 사용해주세요.",
4160
4352
  "standalone.startFailed": "로컬 standalone 서버를 시작하지 못했습니다.",
@@ -4235,7 +4427,6 @@ const ko = {
4235
4427
  // Session reconnect / interrupt / delete flow
4236
4428
  "session.recreatedButAssignFailed": "대화는 다시 만들었지만 프로젝트 연결 확인에 실패했습니다. {detail}",
4237
4429
  "session.continuedNew": "기존 대화 연결이 끊겨 새 대화로 이어서 보냈습니다.",
4238
- "session.reactivated": "기존 대화를 다시 활성화했습니다.",
4239
4430
  "session.reconnectNoMachine": "대화를 다시 연결할 머신 정보를 찾을 수 없습니다.",
4240
4431
  "session.cannotReconnect": "이 대화는 다시 연결할 수 없어 기존 요청에 답변할 수 없습니다. 새 대화에서 이어가세요.",
4241
4432
  "session.recreateNoProject": "대화를 다시 만들 프로젝트 정보를 찾을 수 없습니다.",
@@ -4420,15 +4611,9 @@ const ko = {
4420
4611
  "machinePicker.label": "머신",
4421
4612
  "machinePicker.addMachine": "머신 추가",
4422
4613
  "machinePicker.noMachines": "등록된 머신이 없습니다. 먼저 머신을 등록해 주세요.",
4423
- "machinePicker.onlyMachine": "{name} · 이 머신에서 시작합니다",
4614
+ "machinePicker.noOnlineMachines": "온라인 머신이 없습니다. 머신을 켜거나 연결해 주세요.",
4424
4615
  "machinePicker.online": "온라인",
4425
4616
  "machinePicker.offline": "지금 꺼져 있음",
4426
- "machinePicker.recommended": "추천",
4427
- "machinePicker.reasonOnlyOnline": "온라인인 유일한 머신이에요",
4428
- "machinePicker.reasonLeastLoaded": "가장 여유 있는 머신이에요",
4429
- "machinePicker.loadLight": "여유 있음",
4430
- "machinePicker.loadModerate": "보통",
4431
- "machinePicker.loadHeavy": "혼잡",
4432
4617
  // Machine dialog / deploy URL bar / project detail / memory layer
4433
4618
  "machineDialog.selectOnline": "새 대화를 시작할 온라인 머신을 선택하세요.",
4434
4619
  "deployUrlBar.copyUrl": "URL 복사",
@@ -4466,7 +4651,6 @@ const ko = {
4466
4651
  "projectSection.communityTitle": "프로젝트 커뮤니티",
4467
4652
  "projectSection.communityLoading": "커뮤니티 프로젝트를 불러오는 중입니다...",
4468
4653
  "projectSection.communityEmpty": "공개된 커뮤니티 프로젝트가 없습니다.",
4469
- "projectSection.searchEmpty": "검색 결과가 없습니다.",
4470
4654
  "preview.tokenFailedStatus": "Preview 토큰 발급 실패 ({status})",
4471
4655
  "preview.tokenFailed": "Preview 토큰 발급에 실패했습니다.",
4472
4656
  "preview.tokenUrlInvalid": "Preview 토큰 URL이 올바르지 않습니다.",
@@ -4555,7 +4739,7 @@ const ko = {
4555
4739
  "chat.noSessionTitle": "대화가 없습니다",
4556
4740
  "chat.noSessionBody": "이 프로젝트에는 아직 연결된 대화가 없습니다.",
4557
4741
  "chat.loadingOlder": "불러오는 중...",
4558
- "chat.loadOlder": "이전 대화 불러오기",
4742
+ "chat.loadOlder": "이전 대화",
4559
4743
  "chat.search.newer": "새로운 결과",
4560
4744
  "chat.search.close": "찾기 닫기",
4561
4745
  "chat.search.partial": "이전 메시지 더 있음",
@@ -4606,11 +4790,6 @@ const ko = {
4606
4790
  "agentActivity.waiting": "응답을 기다리는 중",
4607
4791
  "agentActivity.logLines": "로그 {count}줄",
4608
4792
  "agentActivity.elapsed": "경과 {duration}",
4609
- "agentActivity.took": "{duration} 소요",
4610
- "agentActivity.complete": "응답 완료",
4611
- "agentActivity.refreshAria": "대화 새로고침",
4612
- "agentActivity.refreshTitle": "대화 영역만 새로고침",
4613
- "agentActivity.refresh": "새로고침",
4614
4793
  "toolCall.answerSubmitted": "답변 제출됨",
4615
4794
  "toolCall.originalTool": "원본 도구",
4616
4795
  "toolCall.resultImages": "도구가 돌려준 이미지",
@@ -4647,7 +4826,6 @@ const ko = {
4647
4826
  "templates.required": "시작할 템플릿을 선택해 주세요.",
4648
4827
  "templates.onlineOnly": "템플릿 프로젝트는 온라인 머신에서만 시작할 수 있습니다.",
4649
4828
  "templates.unknown": "선택한 템플릿을 찾을 수 없습니다.",
4650
- "templates.summary": "검증된 템플릿 파일로 프로젝트를 만들고, 첫 프롬프트를 입력창에 채웁니다.",
4651
4829
  "templates.importedNotice": "'{name}' 프로젝트를 템플릿에서 만들었습니다. 파일 {count}개를 준비했고 첫 프롬프트를 입력창에 채웠습니다.",
4652
4830
  "templates.partialNotice": "'{name}' 프로젝트는 생성했지만 템플릿 적용이 완전하지 않습니다. {detail}",
4653
4831
  "templates.galleryLabel": "프로젝트 템플릿",
@@ -4818,7 +4996,7 @@ const ko = {
4818
4996
  "workspace.loadLatestFailed": "최신 워크스페이스 정보를 불러오지 못했습니다.",
4819
4997
  "workspace.refreshFailed": "워크스페이스를 새로고침하지 못했습니다.",
4820
4998
  "workspace.responseStalled": "에이전트가 {seconds}초 넘게 새 출력을 내지 않고 있습니다. 긴 작업(빌드·테스트) 중일 수 있어 응답 대기를 유지합니다.",
4821
- "workspace.syncPausedNetwork": "네트워크 문제로 응답 동기화가 중단되었습니다. 연결 복구 후 대화 새로고침으로 최신 응답을 확인해 주세요.",
4999
+ "workspace.syncPausedNetwork": "네트워크 문제로 응답 동기화가 중단되었습니다. 네트워크 연결 상태를 확인해 주세요.",
4822
5000
  "workspace.syncResumed": "네트워크가 복구되어 응답 동기화가 재개되었습니다.",
4823
5001
  "workspace.syncDelayedNetwork": "네트워크 문제로 응답 동기화가 지연되고 있습니다. 연결이 복구되면 자동으로 이어집니다.",
4824
5002
  "workspace.syncStoppedNoSession": "대화 메시지를 찾을 수 없어 응답 대기를 중지했습니다. 새 대화에서 이어가세요.",
@@ -4966,6 +5144,7 @@ const ko = {
4966
5144
  "terminalWorkspace.terminated": "터미널이 종료되었습니다.",
4967
5145
  "terminalWorkspace.unpin": "고정 해제",
4968
5146
  "terminalWorkspace.pin": "고정",
5147
+ "terminalWorkspace.pinDescription": "숨겨진 터미널의 유휴 자동 종료와 자원 회수를 방지합니다.",
4969
5148
  "terminalWorkspace.endTerminal": "터미널 종료",
4970
5149
  // SidebarProjectFiles
4971
5150
  "sidebarFiles.title": "프로젝트 파일",
@@ -5063,6 +5242,23 @@ const ko = {
5063
5242
  "connectors.apiKeySaving": "검증 중…",
5064
5243
  "connectors.apiKeyFailed": "API Key를 확인하지 못했습니다. 키와 meetings:read 권한을 확인해 주세요.",
5065
5244
  "connectors.googleOrgOnly": "Google 커넥터는 회사 계정으로만 연결할 수 있습니다.",
5245
+ "integrations.title": "연동",
5246
+ "integrations.description": "즐겨 쓰는 서비스와 Saycode를 연결해 대화에서 활용하세요.",
5247
+ "integrations.localDescription": "이 기기에서 사용할 확장을 설치하고 관리하세요. 온라인 계정 연동은 Cloud 모드에서 사용할 수 있습니다.",
5248
+ "integrations.connected": "연결됨",
5249
+ "integrations.noConnections": "아직 연결된 서비스가 없습니다.",
5250
+ "integrations.available": "사용 가능한 연동",
5251
+ "integrations.search": "연동 검색",
5252
+ "integrations.noResults": "검색 결과가 없습니다.",
5253
+ "integrations.statusFailed": "커넥터 상태를 확인하지 못했습니다. 새로고침해서 다시 시도하세요.",
5254
+ "integrations.confirmDisconnect": "연결 해제 확인",
5255
+ "integrations.provider.notion": "문서와 위키를 검색하고 불러옵니다.",
5256
+ "integrations.provider.slack": "채널과 메시지를 검색하고 읽습니다.",
5257
+ "integrations.provider.google-drive": "Drive 파일을 검색하고 읽습니다.",
5258
+ "integrations.provider.gmail": "메일과 스레드를 검색하고 읽습니다.",
5259
+ "integrations.provider.knoi": "회의와 기록을 검색하고 읽습니다.",
5260
+ "integrations.provider.github": "저장소 가져오기와 PR 작업에 사용합니다.",
5261
+ "integrations.provider.gitlab": "저장소 가져오기와 PR 작업에 사용합니다.",
5066
5262
  "remoteDir.emptyResponse": "머신이 빈 디렉토리 목록 응답을 반환했습니다.",
5067
5263
  "remoteDir.listFailed": "원격 디렉토리 목록을 불러오지 못했습니다.",
5068
5264
  "remoteFile.sha256Failed": "SHA-256를 계산할 수 없습니다 (crypto.subtle 사용 불가).",
@@ -5078,7 +5274,7 @@ const ko = {
5078
5274
  "codexRecovery.limit-reached": "적용한 계정도 사용 한도에 도달했습니다. 여유가 있는 계정으로 로그인한 뒤 다시 적용하세요.",
5079
5275
  "codexRecovery.probe.locked": "이 대화의 암호화 키를 사용할 수 없습니다. Saycode 로그인 상태와 대화 접근 권한을 확인하세요.",
5080
5276
  "codexRecovery.probe.connection": "대화 실행 프로세스에 연결하지 못했습니다. 연결 상태를 확인하고 상태를 다시 조회하세요.",
5081
- "codexRecovery.probe.missing": "이 대화의 실행 프로세스가 복구 요청을 받지 못했습니다. CLI 설치와 별개로 기존 프로세스가 남아 있거나 종료됐을 수 있습니다. 대기 중인 대화라면 대화 계속하기로 재개한 뒤 상태를 다시 확인하세요.",
5277
+ "codexRecovery.probe.missing": "이 대화의 실행 프로세스가 복구 요청을 받지 못했습니다. CLI 설치와 별개로 기존 프로세스가 남아 있거나 종료됐을 수 있습니다. 대기 중인 대화라면 메시지를 보내 재개한 뒤 상태를 다시 확인하세요.",
5082
5278
  "codexRecovery.probe.invalid": "복구 상태 응답을 해석하지 못했습니다. 대화의 암호화 상태와 실행 중인 런타임의 호환성을 확인하세요.",
5083
5279
  "codexRecovery.probe.changed": "조회 중 로그인 또는 대화 암호화 상태가 바뀌었습니다. 상태를 다시 확인하세요.",
5084
5280
  "codexRecovery.continuePrompt": "이어서 진행해줘.",
@@ -5148,14 +5344,8 @@ const ko = {
5148
5344
  ...previewTunnelKo,
5149
5345
  ...recoveryPasskeyKo,
5150
5346
  // Composer / ask-user / workspace defaults + git status (non-hook + App composer)
5151
- "composer.endedSessionNotice": "이 대화는 잠시 대기 중이에요. 메시지를 보내면 자동으로 이어서 계속됩니다.",
5152
5347
  "session.badge.standby": "대기",
5153
5348
  "session.badge.done": "완료",
5154
- "composer.endedSessionAria": "종료된 대화 복구",
5155
- "composer.continueConversation": "대화 계속하기",
5156
- "composer.continuingConversation": "대화 복구 중...",
5157
- "composer.startNewConversation": "새 대화 시작",
5158
- "composer.recoveryFailed": "기존 대화를 다시 열지 못했습니다. 새 대화를 시작해 이어가세요. ({detail})",
5159
5349
  "askUser.otherLabel": "기타 (직접 입력)",
5160
5350
  "askUser.questionFallback": "질문 {index}",
5161
5351
  "age.unknown": "알 수 없음",
@@ -13660,7 +13850,7 @@ function defaultEffortForAgent(agent, model) {
13660
13850
  const newSessionAgents = [
13661
13851
  { id: "claude", label: "Claude", provider: "Anthropic" },
13662
13852
  { id: "codex", label: "Codex", provider: "OpenAI" },
13663
- { id: "opencode", label: "opencode", provider: "opencode" },
13853
+ { id: "opencode", label: "Opencode", provider: "opencode" },
13664
13854
  { id: "grok", label: "Grok", provider: "xAI" }
13665
13855
  ];
13666
13856
  function parseStarterAgent(value2) {
@@ -18148,7 +18338,7 @@ function requireWebsocket() {
18148
18338
  const EventEmitter = require$$0$6;
18149
18339
  const https = require$$4;
18150
18340
  const http = require$$3;
18151
- const net = require$$3$1;
18341
+ const net2 = require$$3$1;
18152
18342
  const tls = require$$4$1;
18153
18343
  const { randomBytes: randomBytes2, createHash: createHash2 } = require$$0;
18154
18344
  const { Duplex, Readable } = require$$0$5;
@@ -18892,12 +19082,12 @@ function requireWebsocket() {
18892
19082
  }
18893
19083
  function netConnect(options) {
18894
19084
  options.path = options.socketPath;
18895
- return net.connect(options);
19085
+ return net2.connect(options);
18896
19086
  }
18897
19087
  function tlsConnect(options) {
18898
19088
  options.path = void 0;
18899
19089
  if (!options.servername && options.servername !== "") {
18900
- options.servername = net.isIP(options.host) ? "" : options.host;
19090
+ options.servername = net2.isIP(options.host) ? "" : options.host;
18901
19091
  }
18902
19092
  return tls.connect(options);
18903
19093
  }
@@ -29858,6 +30048,7 @@ async function captureCliVoicePrompt(context, voiceFile, waitForStop) {
29858
30048
  }
29859
30049
  }
29860
30050
  const USAGE_SETTINGS_NAVIGATION_DESTINATIONS = [
30051
+ "settings_onboarding",
29861
30052
  "settings_profile",
29862
30053
  "settings_system_prompt",
29863
30054
  "settings_personal_memory",
@@ -29927,6 +30118,7 @@ const USAGE_NAVIGATION_DESTINATIONS = [
29927
30118
  "all_conversations",
29928
30119
  "machines",
29929
30120
  "organization_home",
30121
+ "integrations",
29930
30122
  "settings",
29931
30123
  "organization_admin",
29932
30124
  "system_admin",
@@ -33580,6 +33772,7 @@ function createHostController(deps) {
33580
33772
  if (!who) throw new HostCallError("HOST_CREDENTIAL_UNVERIFIED");
33581
33773
  if (who.scopeKey !== scopeKey2 || who.machineId !== deps.machineId) throw new HostCallError("HOST_SCOPE_MISMATCH");
33582
33774
  if (binding && binding.scopeKey !== who.scopeKey) throw new HostCallError("HOST_SCOPE_MISMATCH");
33775
+ if (binding && binding.userId !== who.userId) throw new HostCallError("HOST_USER_MISMATCH");
33583
33776
  const next = { scopeKey: who.scopeKey, machineId: who.machineId, userId: who.userId, companyId: who.companyId, apiBaseUrl: p.apiBaseUrl };
33584
33777
  token = { token: p.token, expiresAt: p.expiresAt };
33585
33778
  await deps.store.saveToken(token);
@@ -33704,17 +33897,19 @@ function createHostController(deps) {
33704
33897
  };
33705
33898
  }
33706
33899
  async function reconcileOne(spec, deps) {
33707
- if (deps.isRevoked(spec.extensionId, spec.version)) return "revoked";
33708
33900
  let current = deps.registry.get(spec.extensionId);
33709
- if (current?.installedVersion !== spec.version) {
33710
- if (await deps.official.versionOf(spec.extensionId) !== spec.version) return "unavailable";
33901
+ const target = current?.installedVersion === spec.version ? spec.version : await deps.official.versionOf(spec.extensionId);
33902
+ if (!target) return "unavailable";
33903
+ if (deps.isRevoked(spec.extensionId, target)) return "revoked";
33904
+ if (current?.installedVersion !== target) {
33711
33905
  const installed = await deps.store.install(await deps.official.archive(spec.extensionId));
33712
- if (installed.id !== spec.extensionId || installed.version !== spec.version) return "failed";
33713
- current = await deps.registry.apply(spec.extensionId, current ? { type: "update-installed", version: spec.version } : { type: "installed", version: spec.version });
33906
+ if (installed.id !== spec.extensionId || installed.version !== target) return "failed";
33907
+ current = await deps.registry.apply(spec.extensionId, current ? { type: "update-installed", version: target } : { type: "installed", version: target });
33714
33908
  }
33715
33909
  if (!current) return "failed";
33716
33910
  if (!current.enabled) current = await deps.registry.apply(spec.extensionId, { type: "enabled" }) ?? current;
33717
- const wanted = new Set(spec.approvedPermissions);
33911
+ const declared = new Set((await deps.store.readInstalledManifest(spec.extensionId, target)).permissions);
33912
+ const wanted = new Set(spec.approvedPermissions.filter((permission) => declared.has(permission)));
33718
33913
  for (const permission of current.approvedPermissions) {
33719
33914
  if (!wanted.has(permission)) await deps.registry.apply(spec.extensionId, { type: "permission-revoked", permission });
33720
33915
  }
@@ -34189,6 +34384,10 @@ function parseExtensionChannelContributions(value2) {
34189
34384
  });
34190
34385
  }
34191
34386
  const EXTENSION_PERMISSIONS = [
34387
+ "assets.read",
34388
+ "assets.write",
34389
+ "files.select",
34390
+ "drafts.prepare",
34192
34391
  "projects.read",
34193
34392
  "remoteFiles.read",
34194
34393
  "remoteFiles.write",
@@ -34218,7 +34417,7 @@ const EXTENSION_PERMISSIONS = [
34218
34417
  const SAYCODE_EXTENSION_API_VERSION = 3;
34219
34418
  const SAYCODE_EXTENSION_MINIMUM_API_VERSION = 2;
34220
34419
  const MAX_EXTENSION_API_WINDOW_WIDTH = 2;
34221
- const SAYCODE_EXTENSION_ENGINE_VERSION = "1.0.0";
34420
+ const SAYCODE_EXTENSION_ENGINE_VERSION = "1.1.0";
34222
34421
  const SAYCODE_CHANNEL_API_VERSION = CHANNEL_API_VERSION;
34223
34422
  const SAYCODE_CHANNEL_MINIMUM_API_VERSION = CHANNEL_MINIMUM_API_VERSION;
34224
34423
  class UnsupportedChannelApiVersionError2 extends Error {
@@ -34248,7 +34447,7 @@ const V2_PERMISSIONS = /* @__PURE__ */ new Set([
34248
34447
  "browserViewer.install",
34249
34448
  "browserViewer.installChrome"
34250
34449
  ]);
34251
- const V3_PERMISSIONS = /* @__PURE__ */ new Set(["artifacts.publishPublic"]);
34450
+ const V3_PERMISSIONS = /* @__PURE__ */ new Set(["artifacts.publishPublic", "assets.read", "assets.write", "files.select", "drafts.prepare"]);
34252
34451
  const CHANNEL_PERMISSIONS = /* @__PURE__ */ new Set([
34253
34452
  "channels.receive",
34254
34453
  "channels.ack",
@@ -34809,6 +35008,12 @@ async function openChannelHostKey(input) {
34809
35008
  await rename(staging, path);
34810
35009
  return describe(secretKey);
34811
35010
  }
35011
+ const ATTEMPT_MS = 1e3;
35012
+ function widenConnectAttempts() {
35013
+ if (net.getDefaultAutoSelectFamilyAttemptTimeout() < ATTEMPT_MS) {
35014
+ net.setDefaultAutoSelectFamilyAttemptTimeout(ATTEMPT_MS);
35015
+ }
35016
+ }
34812
35017
  function defaultIsAlive(pid) {
34813
35018
  try {
34814
35019
  process.kill(pid, 0);
@@ -45213,7 +45418,7 @@ function resolvePersonalChatWorkspaceDirectory(input) {
45213
45418
  machineHomeDir: input.machine.homeDir,
45214
45419
  userId: input.userId
45215
45420
  });
45216
- return userDirectory ? `${userDirectory}/chats/${input.chatId}` : null;
45421
+ return userDirectory ? appendDirectorySegments(userDirectory, ["chats", input.chatId]) : null;
45217
45422
  }
45218
45423
  async function createPersonalChatSession(input, deps = {}) {
45219
45424
  if (!input.machine.online) throw new Error(translateCurrent("sessionCreation.machineOfflineSelected"));
@@ -45254,9 +45459,9 @@ async function createPersonalChatSession(input, deps = {}) {
45254
45459
  }
45255
45460
  }
45256
45461
  const REAL_HAPPY_SESSION_CONNECTION_DEPS = {
45257
- reconnectMcp: async (input) => (await import("./assets/sessionMcpReconnect-BwLavGVX.js")).reconnectDesktopSessionMcp(input),
45258
- readCodexAuthStatus: (input) => import("./assets/codexAuthRecoveryRpc-CSfqOzQr.js").then((module) => module.readCodexAuthStatus(input)),
45259
- recoverCodexAuth: (input) => import("./assets/codexAuthRecoveryRpc-CSfqOzQr.js").then((module) => module.recoverCodexAuth(input)),
45462
+ reconnectMcp: async (input) => (await import("./assets/sessionMcpReconnect-DC2ffWe8.js")).reconnectDesktopSessionMcp(input),
45463
+ readCodexAuthStatus: (input) => import("./assets/codexAuthRecoveryRpc-DZubmLWa.js").then((module) => module.readCodexAuthStatus(input)),
45464
+ recoverCodexAuth: (input) => import("./assets/codexAuthRecoveryRpc-DZubmLWa.js").then((module) => module.recoverCodexAuth(input)),
45260
45465
  prompt: async ({ auth, sessionId, ...input }) => {
45261
45466
  const encryption = resolveSessionWriteEncryption(sessionId);
45262
45467
  if (encryption.kind === "blocked") throw new SessionWriteLockedError();
@@ -48924,6 +49129,10 @@ class CapabilityBroker {
48924
49129
  return assertSafeExtensionDto(result);
48925
49130
  }
48926
49131
  }
49132
+ const ASSET_PERMISSIONS = ["assets.read", "assets.write", "files.select", "drafts.prepare"];
49133
+ function usesAssetWorkflow(permissions) {
49134
+ return permissions.some((permission) => ASSET_PERMISSIONS.includes(permission));
49135
+ }
48927
49136
  class ExtensionHostProcessError extends Error {
48928
49137
  constructor(code, message) {
48929
49138
  super(message);
@@ -49262,6 +49471,7 @@ class ExtensionHostManager {
49262
49471
  const broker = this.options.capabilityBrokerFor?.(manifest2);
49263
49472
  let process2;
49264
49473
  process2 = new ExtensionHostProcess({
49474
+ requestTimeoutMs: usesAssetWorkflow(manifest2.permissions) ? 14e4 : void 0,
49265
49475
  apiVersion: manifest2.apiVersion,
49266
49476
  extensionId: manifest2.id,
49267
49477
  extensionVersion: manifest2.version,
@@ -59531,6 +59741,15 @@ function fenceLeaseByProvider(inner, input) {
59531
59741
  stop: () => inner.stop()
59532
59742
  };
59533
59743
  }
59744
+ function createAdmittedProviders() {
59745
+ const providers = /* @__PURE__ */ new Map();
59746
+ return {
59747
+ admit: (connectionId2, provider2) => {
59748
+ providers.set(connectionId2, provider2);
59749
+ },
59750
+ providerOf: async (connectionId2) => providers.get(connectionId2) ?? null
59751
+ };
59752
+ }
59534
59753
  const FILE = "session-keys.v1.json";
59535
59754
  const DEFAULT_LIMIT = 500;
59536
59755
  function createHostSessionKeyStore(input) {
@@ -59706,7 +59925,14 @@ function hostAuthSession(init, binding, token) {
59706
59925
  activeCompanyId: binding.companyId
59707
59926
  };
59708
59927
  }
59709
- function hostMachine(init, now) {
59928
+ const REGISTRY_TYPES = /* @__PURE__ */ new Set(["personal", "dedicated", "shared"]);
59929
+ function parseHostMachineRegistry(value2) {
59930
+ if (!value2 || typeof value2 !== "object") return null;
59931
+ const { type, workspaceRoot } = value2;
59932
+ if (typeof type !== "string" || !REGISTRY_TYPES.has(type)) return null;
59933
+ return { type, ...typeof workspaceRoot === "string" && workspaceRoot ? { workspaceRoot } : {} };
59934
+ }
59935
+ function hostMachine(init, now, registry2) {
59710
59936
  return {
59711
59937
  ...init.machine.capabilities ?? {},
59712
59938
  id: init.machine.id,
@@ -59714,7 +59940,8 @@ function hostMachine(init, now) {
59714
59940
  platform: init.machine.platform,
59715
59941
  homeDir: init.machine.homeDir,
59716
59942
  online: true,
59717
- activeAt: now
59943
+ activeAt: now,
59944
+ ...registry2 ? { registry: registry2 } : {}
59718
59945
  };
59719
59946
  }
59720
59947
  function adoptReplyKey(sessionId, reply) {
@@ -59745,6 +59972,7 @@ async function openHostRuntime(input) {
59745
59972
  await candidates2.refresh();
59746
59973
  const usableCandidates = () => input.sandbox ? candidates2.candidates() : [];
59747
59974
  let disabledProviders = /* @__PURE__ */ new Set();
59975
+ const providers = createAdmittedProviders();
59748
59976
  const ownerId = `host-${createHash("sha256").update(`${process.pid}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 24)}`;
59749
59977
  let runtime = null;
59750
59978
  const leases = createChannelGatewayLeaseSupervisor({
@@ -59756,18 +59984,14 @@ async function openHostRuntime(input) {
59756
59984
  monotonicNow: () => performance.now(),
59757
59985
  fetch: (url2, init) => fetch(url2, init)
59758
59986
  }), {
59759
- providerOf: async () => {
59760
- const snapshot = await runtime?.settingsServiceFor(auth())?.call("channels:snapshot", null);
59761
- const entry = snapshot ? Object.values(snapshot.connections).find((connection) => connection.connectionId === connectionId2) : void 0;
59762
- return entry?.provider ?? null;
59763
- },
59987
+ providerOf: () => providers.providerOf(connectionId2),
59764
59988
  disabled: () => disabledProviders
59765
59989
  }),
59766
59990
  stopReceiving: (connectionId2) => runtime?.pauseReceivingFor(connectionId2) ?? Promise.resolve(),
59767
59991
  resumeReceiving: (connectionId2) => runtime?.resumeReceivingFor(connectionId2) ?? Promise.resolve(),
59768
59992
  onDiagnostic: ({ code }) => input.report(code)
59769
59993
  });
59770
- const machine = () => hostMachine(input.init, Date.now());
59994
+ const machine = async () => hostMachine(input.init, Date.now(), await input.machineRegistry().catch(() => null));
59771
59995
  const sessionKeys = createHostSessionKeyStore({ dir: dataDir, safeStorage: input.safeStorage });
59772
59996
  const keep = (sessionId, reply) => {
59773
59997
  const key = adoptReplyKey(sessionId, reply);
@@ -59784,7 +60008,7 @@ async function openHostRuntime(input) {
59784
60008
  if (reply) keep(sessionId, reply);
59785
60009
  };
59786
60010
  const base2 = createChannelSessionOperations({
59787
- listMachines: async () => [machine()],
60011
+ listMachines: async () => [await machine()],
59788
60012
  spawn: async (spawnInput) => {
59789
60013
  const reply = await input.bridge.request("spawn", {
59790
60014
  directory: spawnInput.directory,
@@ -59838,7 +60062,10 @@ async function openHostRuntime(input) {
59838
60062
  isEstopEngaged: async () => input.isPaused(),
59839
60063
  mayActForConnection: (connectionId2) => leases.holdsLease(connectionId2),
59840
60064
  standingBy: () => leases.standingBy(),
59841
- admitReceiving: ({ connectionId: connectionId2, provider: provider2, installationId }) => leases.admit({ connectionId: connectionId2, connectionDigest: channelLeaseSubject({ provider: provider2, installationId }) }),
60065
+ admitReceiving: ({ connectionId: connectionId2, provider: provider2, installationId }) => {
60066
+ providers.admit(connectionId2, provider2);
60067
+ return leases.admit({ connectionId: connectionId2, connectionDigest: channelLeaseSubject({ provider: provider2, installationId }) });
60068
+ },
59842
60069
  authorizeExecution: (request) => deliverDesktopChannelAuthorize(request),
59843
60070
  cancelExecution: (request) => deliverDesktopChannelCancel(request),
59844
60071
  formatReply: async (request, isCurrent) => {
@@ -59925,8 +60152,8 @@ async function openHostRuntime(input) {
59925
60152
  return service.call(call, payload);
59926
60153
  },
59927
60154
  refreshAuthority: adoptAuthority,
59928
- setDisabledProviders(providers) {
59929
- disabledProviders = new Set(providers);
60155
+ setDisabledProviders(providers2) {
60156
+ disabledProviders = new Set(providers2);
59930
60157
  },
59931
60158
  async refreshAdapters() {
59932
60159
  await registry2.load();
@@ -60122,7 +60349,13 @@ async function whoami(apiBaseUrl, token) {
60122
60349
  if (typeof body.scopeKey !== "string" || typeof body.machineId !== "string" || typeof body.userId !== "string") {
60123
60350
  throw new Error("HOST_CREDENTIAL_UNVERIFIED");
60124
60351
  }
60125
- return { scopeKey: body.scopeKey, machineId: body.machineId, userId: body.userId, companyId: typeof body.companyId === "string" ? body.companyId : null };
60352
+ return {
60353
+ scopeKey: body.scopeKey,
60354
+ machineId: body.machineId,
60355
+ userId: body.userId,
60356
+ companyId: typeof body.companyId === "string" ? body.companyId : null,
60357
+ machine: parseHostMachineRegistry(body.machine)
60358
+ };
60126
60359
  }
60127
60360
  async function runChannelHost() {
60128
60361
  const lines = createInterface({ input: process.stdin });
@@ -60148,6 +60381,7 @@ async function runChannelHost() {
60148
60381
  });
60149
60382
  if (!init) return unavailable2("INIT_INVALID");
60150
60383
  if (!nodeSupportsPermission(process.version)) return unavailable2("NODE_TOO_OLD");
60384
+ widenConnectAttempts();
60151
60385
  await mkdir(init.dataDir, { recursive: true, mode: 448 });
60152
60386
  const lock = await acquireChannelHostLock({ dir: init.dataDir });
60153
60387
  if (lock.kind !== "held") return unavailable2("LOCK_HELD");
@@ -60172,7 +60406,8 @@ async function runChannelHost() {
60172
60406
  whoami,
60173
60407
  serverFor: (binding, token) => createHostServerClient({ apiBaseUrl: binding.apiBaseUrl, token }),
60174
60408
  openRuntime: async ({ binding, token, isPaused }) => {
60175
- opened = await openHostRuntime({ init, binding, token, isPaused, safeStorage, bridge, sandbox, extensionRoot: layout?.extensionRoot ?? init.extensionsDir, bootstrapPath, report });
60409
+ const machineRegistry = async () => (await whoami(binding.apiBaseUrl, token())).machine;
60410
+ opened = await openHostRuntime({ init, binding, token, isPaused, safeStorage, bridge, sandbox, extensionRoot: layout?.extensionRoot ?? init.extensionsDir, bootstrapPath, report, machineRegistry });
60176
60411
  catalog2 = createRemoteExtensionCatalog({
60177
60412
  cachePath: join(init.dataDir, "extension-catalog.v1.json"),
60178
60413
  revocations: opened.extensions.revocations,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buzzni/saycode-cli",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Drive Saycode sessions across your machines from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {