nextclaw 0.48.0-beta.1 → 0.48.0-beta.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.
Files changed (112) hide show
  1. package/dist/cli/app/index.js +1 -1
  2. package/dist/{nextclaw-cli-app-cRvTVj04.js → nextclaw-cli-app-FS6IsYq6.js} +59 -1
  3. package/dist/{nextclaw-cli-app-cRvTVj04.js.map → nextclaw-cli-app-FS6IsYq6.js.map} +1 -1
  4. package/package.json +16 -16
  5. package/resources/apps/nextclaw-portable-runtime-lab/manifest.json +13 -3
  6. package/ui-dist/assets/api-BqVIq0lF.js +13 -0
  7. package/ui-dist/assets/api-BqVIq0lF.js.gz +0 -0
  8. package/ui-dist/assets/{appearance-settings-page-CPoFZLw8.js → appearance-settings-page-CTWn1eAT.js} +1 -1
  9. package/ui-dist/assets/appearance-settings-page-CTWn1eAT.js.gz +0 -0
  10. package/ui-dist/assets/{book-open-jcUt0rr9.js → book-open-o2x7OLiO.js} +1 -1
  11. package/ui-dist/assets/{channels-list-page-anMJAkfK.js → channels-list-page-Bzn-pzyJ.js} +1 -1
  12. package/ui-dist/assets/channels-list-page-Bzn-pzyJ.js.gz +0 -0
  13. package/ui-dist/assets/{config-split-page-AcuFnbIC.js → config-split-page-CnRbXXlh.js} +1 -1
  14. package/ui-dist/assets/config-split-page-CnRbXXlh.js.gz +0 -0
  15. package/ui-dist/assets/{confirm-dialog-B-atCfC5.js → confirm-dialog-B_Hbcra2.js} +1 -1
  16. package/ui-dist/assets/confirm-dialog-B_Hbcra2.js.gz +0 -0
  17. package/ui-dist/assets/{createLucideIcon-Blz0FzrV.js → createLucideIcon-rwwpqyks.js} +1 -1
  18. package/ui-dist/assets/{desktop-capabilities-page-8lKF_Ob-.js → desktop-capabilities-page-C2ayYQTr.js} +1 -1
  19. package/ui-dist/assets/desktop-capabilities-page-C2ayYQTr.js.gz +0 -0
  20. package/ui-dist/assets/{desktop-update-config-DABXDpbj.js → desktop-update-config-C13czpyL.js} +1 -1
  21. package/ui-dist/assets/desktop-update-config-C13czpyL.js.gz +0 -0
  22. package/ui-dist/assets/{dist-eH-_j8yU.js → dist-BsF3hfOi.js} +1 -1
  23. package/ui-dist/assets/dist-BsF3hfOi.js.gz +0 -0
  24. package/ui-dist/assets/{dist-D8kcEpTf.js → dist-aNN3iQxV.js} +1 -1
  25. package/ui-dist/assets/dist-aNN3iQxV.js.gz +0 -0
  26. package/ui-dist/assets/{doc-browser-DzXRehlW.js → doc-browser-D8dIOo9E.js} +1 -1
  27. package/ui-dist/assets/doc-browser-D8dIOo9E.js.gz +0 -0
  28. package/ui-dist/assets/doc-browser-DcuOOtpP.js +1 -0
  29. package/ui-dist/assets/{doc-browser-context-BlbUcg2J.js → doc-browser-context-Dhsit2Ma.js} +1 -1
  30. package/ui-dist/assets/doc-browser-context-Dhsit2Ma.js.gz +0 -0
  31. package/ui-dist/assets/{extensions-mquwAJ2Z.js → extensions-CD6xvKF_.js} +1 -1
  32. package/ui-dist/assets/extensions-CD6xvKF_.js.gz +0 -0
  33. package/ui-dist/assets/{form-actions-CaZDGEmB.js → form-actions-CC4Wz_lL.js} +1 -1
  34. package/ui-dist/assets/index-BTsbdtv6.js +109 -0
  35. package/ui-dist/assets/index-BTsbdtv6.js.gz +0 -0
  36. package/ui-dist/assets/index-Bcfy2kTh.css +1 -0
  37. package/ui-dist/assets/index-Bcfy2kTh.css.gz +0 -0
  38. package/ui-dist/assets/loader-circle-B6aFbB-8.js +1 -0
  39. package/ui-dist/assets/mcp-marketplace-page-BtUAypA7.js +1 -0
  40. package/ui-dist/assets/{mcp-marketplace-page-gFtgyueL.js → mcp-marketplace-page-r2MmD9xO.js} +1 -1
  41. package/ui-dist/assets/mcp-marketplace-page-r2MmD9xO.js.gz +0 -0
  42. package/ui-dist/assets/{model-config-page-hFBpeRAO.js → model-config-page-DboxoT-I.js} +1 -1
  43. package/ui-dist/assets/model-config-page-DboxoT-I.js.gz +0 -0
  44. package/ui-dist/assets/{navigation-link-Hb5Z0UxZ.js → navigation-link-C_6oj3zz.js} +1 -1
  45. package/ui-dist/assets/navigation-link-C_6oj3zz.js.gz +0 -0
  46. package/ui-dist/assets/plus-DRsq9Kvs.js +1 -0
  47. package/ui-dist/assets/{privacy-settings-page-C18zo3ox.js → privacy-settings-page-C2sDBqhb.js} +1 -1
  48. package/ui-dist/assets/privacy-settings-page-C2sDBqhb.js.gz +0 -0
  49. package/ui-dist/assets/projects-BcPcx-uD.js +1 -0
  50. package/ui-dist/assets/projects-BcPcx-uD.js.gz +0 -0
  51. package/ui-dist/assets/{providers-config-page-YU0-AOXJ.js → providers-config-page-CmUFl9RF.js} +1 -1
  52. package/ui-dist/assets/providers-config-page-CmUFl9RF.js.gz +0 -0
  53. package/ui-dist/assets/react-BjO9I4WZ.js +8 -0
  54. package/ui-dist/assets/react-BjO9I4WZ.js.gz +0 -0
  55. package/ui-dist/assets/{refresh-cw-ZHKfE3yx.js → refresh-cw-DsT2kvXi.js} +1 -1
  56. package/ui-dist/assets/remote-Ci6x91ES.js +1 -0
  57. package/ui-dist/assets/{rotate-cw-CLhOXGwE.js → rotate-cw-BCAs6c3R.js} +1 -1
  58. package/ui-dist/assets/{runtime-config-page-Dmy1A_0Y.js → runtime-config-page-cQKNeAjL.js} +1 -1
  59. package/ui-dist/assets/runtime-config-page-cQKNeAjL.js.gz +0 -0
  60. package/ui-dist/assets/{save-CWuuZs77.js → save-CadRMTMQ.js} +1 -1
  61. package/ui-dist/assets/{search-DcmsKAn2.js → search-B6tB4fzB.js} +1 -1
  62. package/ui-dist/assets/{search-config-page-CxBacx6W.js → search-config-page-C8lOIkxn.js} +1 -1
  63. package/ui-dist/assets/search-config-page-C8lOIkxn.js.gz +0 -0
  64. package/ui-dist/assets/{secrets-config-page-B4py8-KR.js → secrets-config-page-8clzAag2.js} +1 -1
  65. package/ui-dist/assets/secrets-config-page-8clzAag2.js.gz +0 -0
  66. package/ui-dist/assets/security-config-DPbM6IVT.js +1 -0
  67. package/ui-dist/assets/{select-BEr0Iy_f.js → select-CO9S_C2C.js} +1 -1
  68. package/ui-dist/assets/select-CO9S_C2C.js.gz +0 -0
  69. package/ui-dist/assets/skeleton-IHILNhIW.js +1 -0
  70. package/ui-dist/assets/{tag-chip-ByqDkn6g.js → tag-chip-BZKk0UkK.js} +1 -1
  71. package/ui-dist/assets/x-BuvHIb1d.js +1 -0
  72. package/ui-dist/index.html +21 -21
  73. package/ui-dist/index.html.gz +0 -0
  74. package/resources/native/darwin-arm64/nextclaw-wasmtime-runner +0 -0
  75. package/ui-dist/assets/api-CoqPRKmF.js +0 -13
  76. package/ui-dist/assets/api-CoqPRKmF.js.gz +0 -0
  77. package/ui-dist/assets/appearance-settings-page-CPoFZLw8.js.gz +0 -0
  78. package/ui-dist/assets/channels-list-page-anMJAkfK.js.gz +0 -0
  79. package/ui-dist/assets/config-split-page-AcuFnbIC.js.gz +0 -0
  80. package/ui-dist/assets/confirm-dialog-B-atCfC5.js.gz +0 -0
  81. package/ui-dist/assets/desktop-capabilities-page-8lKF_Ob-.js.gz +0 -0
  82. package/ui-dist/assets/desktop-update-config-DABXDpbj.js.gz +0 -0
  83. package/ui-dist/assets/dist-D8kcEpTf.js.gz +0 -0
  84. package/ui-dist/assets/dist-eH-_j8yU.js.gz +0 -0
  85. package/ui-dist/assets/doc-browser-DJEMr7Zk.js +0 -1
  86. package/ui-dist/assets/doc-browser-DzXRehlW.js.gz +0 -0
  87. package/ui-dist/assets/doc-browser-context-BlbUcg2J.js.gz +0 -0
  88. package/ui-dist/assets/extensions-mquwAJ2Z.js.gz +0 -0
  89. package/ui-dist/assets/index-DyR5iR0O.css +0 -1
  90. package/ui-dist/assets/index-DyR5iR0O.css.gz +0 -0
  91. package/ui-dist/assets/index-XxXj8WDS.js +0 -109
  92. package/ui-dist/assets/index-XxXj8WDS.js.gz +0 -0
  93. package/ui-dist/assets/loader-circle-0qPOOYQ5.js +0 -1
  94. package/ui-dist/assets/mcp-marketplace-page-gFtgyueL.js.gz +0 -0
  95. package/ui-dist/assets/mcp-marketplace-page-uanamJ6L.js +0 -1
  96. package/ui-dist/assets/model-config-page-hFBpeRAO.js.gz +0 -0
  97. package/ui-dist/assets/navigation-link-Hb5Z0UxZ.js.gz +0 -0
  98. package/ui-dist/assets/plus-CYOvNvcV.js +0 -1
  99. package/ui-dist/assets/privacy-settings-page-C18zo3ox.js.gz +0 -0
  100. package/ui-dist/assets/projects-C8Gm9RS8.js +0 -1
  101. package/ui-dist/assets/projects-C8Gm9RS8.js.gz +0 -0
  102. package/ui-dist/assets/providers-config-page-YU0-AOXJ.js.gz +0 -0
  103. package/ui-dist/assets/react-DF-kP7iN.js +0 -8
  104. package/ui-dist/assets/react-DF-kP7iN.js.gz +0 -0
  105. package/ui-dist/assets/remote-B_utkBra.js +0 -1
  106. package/ui-dist/assets/runtime-config-page-Dmy1A_0Y.js.gz +0 -0
  107. package/ui-dist/assets/search-config-page-CxBacx6W.js.gz +0 -0
  108. package/ui-dist/assets/secrets-config-page-B4py8-KR.js.gz +0 -0
  109. package/ui-dist/assets/security-config-CTP4IQWw.js +0 -1
  110. package/ui-dist/assets/select-BEr0Iy_f.js.gz +0 -0
  111. package/ui-dist/assets/skeleton-B2NHluWf.js +0 -1
  112. package/ui-dist/assets/x-CENkFyK5.js +0 -1
@@ -5,7 +5,7 @@ import { NextclawDistributionService } from "@nextclaw/service";
5
5
  const distribution = createNextclawDistribution(import.meta.url);
6
6
  repairPackagedPortableRunnerPermissions(distribution);
7
7
  NextclawDistributionService.configureRuntime(distribution);
8
- const { nextclawCliProgram } = await import("../../nextclaw-cli-app-cRvTVj04.js");
8
+ const { nextclawCliProgram } = await import("../../nextclaw-cli-app-FS6IsYq6.js");
9
9
  await nextclawCliProgram.parseAsync(process.argv);
10
10
  //#endregion
11
11
  export {};
@@ -5399,6 +5399,17 @@ var AppPackageLiveService = class {
5399
5399
  }
5400
5400
  list = async () => await this.requireApiClient().request({ path: "/api/app-packages" });
5401
5401
  info = async (appId) => await this.requireApiClient().request({ path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}` });
5402
+ inspectDocumentAccess = async (appId) => await this.requireApiClient().request({ path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}/document-access` });
5403
+ grantDocumentAccess = async (appId, input) => await this.requireApiClient().request({
5404
+ path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}/document-access/grant`,
5405
+ method: "POST",
5406
+ body: input
5407
+ });
5408
+ revokeDocumentAccess = async (appId, scopeId) => await this.requireApiClient().request({
5409
+ path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}/document-access/revoke`,
5410
+ method: "POST",
5411
+ body: { scopeId: this.requireValue(scopeId, "Document scope") }
5412
+ });
5402
5413
  listOperations = async () => await this.requireApiClient().request({ path: "/api/app-package-operations" });
5403
5414
  inspectDependencies = async (appId) => await this.requireApiClient().request({ path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}/dependencies` });
5404
5415
  verifyDependencies = async (appId) => await this.requireApiClient().request({ path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}/dependencies/verify` });
@@ -5551,6 +5562,19 @@ var AppPackageCommandController = class {
5551
5562
  marketplaceInfo = async (selector, options) => this.write(await this.marketplaceService.info(selector), options, this.formatMarketplaceInfo);
5552
5563
  list = async (options) => this.write(await this.liveService.list(), options, this.formatList);
5553
5564
  info = async (appId, options) => this.write(await this.liveService.info(appId), options, this.formatApp);
5565
+ inspectDocumentAccess = async (appId, options) => this.write(await this.liveService.inspectDocumentAccess(appId), options, this.formatDocumentAccess);
5566
+ grantDocumentAccess = async (appId, options) => {
5567
+ await this.liveService.grantDocumentAccess(appId, {
5568
+ scopeId: options.scope,
5569
+ directoryPath: options.path,
5570
+ mode: options.mode
5571
+ });
5572
+ this.write(await this.liveService.inspectDocumentAccess(appId), options, this.formatDocumentAccess);
5573
+ };
5574
+ revokeDocumentAccess = async (appId, options) => {
5575
+ await this.liveService.revokeDocumentAccess(appId, options.scope);
5576
+ this.write(await this.liveService.inspectDocumentAccess(appId), options, this.formatDocumentAccess);
5577
+ };
5554
5578
  inspectDependencies = async (appId, options) => this.write(await this.liveService.inspectDependencies(appId), options, this.formatDependencyView);
5555
5579
  verifyDependencies = async (appId, options) => this.write(await this.liveService.verifyDependencies(appId), options, this.formatDependencyView);
5556
5580
  setupDependencies = async (appId, options) => this.write(await this.liveService.setupDependencies(appId), options, this.formatDependencyView);
@@ -5714,6 +5738,13 @@ var AppPackageCommandController = class {
5714
5738
  ...result.bindings.map((binding) => ` binding: ${binding.componentId}/${binding.requirementKind}/${binding.requirementId} -> ${binding.providerId}`)
5715
5739
  ].join("\n") + "\n";
5716
5740
  formatSecretReadiness = (result) => [`Secret readiness: ${result.readiness.status}`, ...result.slots.map((slot) => ` slot: ${slot.id} ${slot.status}${slot.binding ? ` (${slot.binding.source}/${slot.binding.provider ?? slot.binding.source}/${slot.binding.id})` : ""}${slot.errorCode ? ` [${slot.errorCode}]` : ""}`)].join("\n") + "\n";
5741
+ formatDocumentAccess = (result) => [`Document access for ${result.name} (${result.appId})`, ...result.documentAccess.map((scope) => [
5742
+ ` ${scope.id}: ${scope.status}`,
5743
+ ` declared: ${scope.mode}`,
5744
+ ` granted: ${scope.effectiveMode ?? "-"}`,
5745
+ ` resource: ${scope.grantedPath ?? "-"}`,
5746
+ scope.description ? ` purpose: ${scope.description}` : ""
5747
+ ].filter(Boolean).join("\n"))].join("\n") + "\n";
5717
5748
  formatOperation = (operation) => [
5718
5749
  `App operation ${operation.id}`,
5719
5750
  ` action: ${operation.action}`,
@@ -5836,6 +5867,7 @@ function registerAppPackageCommands(app, appPackages) {
5836
5867
  app.command("info <app-id>").description("Show an App installed in the running NextClaw host").option("--json", "Output JSON", false).action(async (appId, opts) => appPackages.info(appId, opts));
5837
5868
  registerAppDependencyCommands(app, appPackages);
5838
5869
  registerAppSecretCommands(app, appPackages);
5870
+ registerAppPermissionCommands(app, appPackages);
5839
5871
  registerPortableRuntimeAppCommands(app, appPackages);
5840
5872
  app.command("operations").description("List App install, update, rollback, and uninstall operations").option("--json", "Output JSON", false).action(async (opts) => appPackages.operations(opts));
5841
5873
  app.command("install <source>").description("Install an App through the running NextClaw host").option("--registry <url>", "Registry URL for an App id").option("--json", "Output JSON", false).action(async (source, opts) => appPackages.install(source, opts));
@@ -5846,6 +5878,13 @@ function registerAppPackageCommands(app, appPackages) {
5846
5878
  app.command("rollback <app-id>").description("Roll back an App to an installed version").requiredOption("--version <version>", "Installed version to activate").option("--json", "Output JSON", false).action(async (appId, opts) => appPackages.rollback(appId, opts));
5847
5879
  app.command("uninstall <app-id>").description("Uninstall an App through the running NextClaw host").option("--purge-data", "Permanently delete App data", false).option("--confirm <app-id>", "Confirm the exact App id when purging data").option("--json", "Output JSON", false).action(async (appId, opts) => appPackages.uninstall(appId, opts));
5848
5880
  }
5881
+ function registerAppPermissionCommands(app, controller) {
5882
+ const permissions = app.command("permissions").description("Inspect and manage an App's user-approved permissions");
5883
+ permissions.command("inspect <app-id>").description("Show declared document scopes and current grants").option("--json", "Output JSON", false).action(async (appId, opts) => controller.inspectDocumentAccess(appId, opts));
5884
+ const document = permissions.command("document").description("Grant or revoke access to a runtime-host directory");
5885
+ document.command("grant <app-id>").description("Grant or replace one declared document scope").requiredOption("--scope <id>", "Declared document scope id").requiredOption("--path <path>", "Directory path on the runtime host").requiredOption("--mode <mode>", "Effective mode: read or read-write").option("--json", "Output JSON", false).action(async (appId, opts) => controller.grantDocumentAccess(appId, opts));
5886
+ document.command("revoke <app-id>").description("Revoke one declared document scope").requiredOption("--scope <id>", "Declared document scope id").option("--json", "Output JSON", false).action(async (appId, opts) => controller.revokeDocumentAccess(appId, opts));
5887
+ }
5849
5888
  function registerAppSecretCommands(app, controller) {
5850
5889
  const secrets = app.command("secrets").description("Inspect and manage non-sensitive SecretRef bindings for an App");
5851
5890
  secrets.command("inspect <app-id>").description("Show declared Secret slots and non-sensitive bindings").option("--json", "Output JSON", false).action(async (appId, opts) => controller.inspectSecrets(appId, opts));
@@ -5881,6 +5920,25 @@ function registerProjectCommands(program, nextclaw) {
5881
5920
  projects.command("templates").description("List built-in project templates").option("--json", "Output JSON", false).action((options) => commands.templates(options));
5882
5921
  projects.command("observe <root-path>").description("Observe a registered project without changing project data").option("--json", "Output JSON", false).action((rootPath, options) => commands.observe(rootPath, options));
5883
5922
  projects.command("create <name>").description("Create and register a project").option("--path <directory>", "Target directory").option("--template <template>", "Project template: empty or knowledge-base", "empty").option("--json", "Output JSON", false).action((name, options) => commands.create(name, options));
5923
+ registerProjectWorkCommands(projects, commands);
5924
+ }
5925
+ function registerProjectWorkCommands(projects, commands) {
5926
+ const work = projects.command("work").description("Manage persistent project work items");
5927
+ work.command("list").description("List work items").requiredOption("--project <id>", "Project id").option("--include-deleted", "Include deleted work items", false).option("--json", "Output JSON", false).action((options) => commands.workList(options));
5928
+ work.command("get <work-item-id>").description("Show a work item").requiredOption("--project <id>", "Project id").option("--json", "Output JSON", false).action((workItemId, options) => commands.workGet(workItemId, options));
5929
+ work.command("create <title>").description("Create a work item").requiredOption("--project <id>", "Project id").option("--description <text>", "Description").option("--state <id>", "Initial state id").option("--attention <value>", "Attention: none, blocked, or awaiting-user").option("--json", "Output JSON", false).action((title, options) => commands.workCreate(title, options));
5930
+ work.command("update <work-item-id>").description("Update fields or move a work item").requiredOption("--project <id>", "Project id").option("--title <text>", "New title").option("--description <text>", "New description").option("--state <id>", "New state id").option("--attention <value>", "Attention: none, blocked, or awaiting-user").option("--version <number>", "Expected version").option("--json", "Output JSON", false).action((workItemId, options) => commands.workUpdate(workItemId, options));
5931
+ work.command("delete <work-item-id>").description("Soft-delete a work item").requiredOption("--project <id>", "Project id").option("--json", "Output JSON", false).action((workItemId, options) => commands.workDelete(workItemId, options));
5932
+ work.command("restore <work-item-id>").description("Restore a deleted work item").requiredOption("--project <id>", "Project id").option("--json", "Output JSON", false).action((workItemId, options) => commands.workRestore(workItemId, options));
5933
+ work.command("activity <work-item-id>").description("Show immutable work item activity").requiredOption("--project <id>", "Project id").option("--limit <number>", "Maximum entries", "50").option("--json", "Output JSON", false).action((workItemId, options) => commands.workActivity(workItemId, options));
5934
+ const artifact = work.command("artifact").description("Manage work item artifact links");
5935
+ artifact.command("link <work-item-id> <path>").description("Link a project file to a work item").requiredOption("--project <id>", "Project id").option("--label <text>", "Display label").option("--json", "Output JSON", false).action((workItemId, path, options) => commands.workArtifactLink(workItemId, path, options));
5936
+ artifact.command("unlink <work-item-id> <artifact-link-id>").description("Remove a work item artifact link").requiredOption("--project <id>", "Project id").option("--json", "Output JSON", false).action((workItemId, artifactLinkId, options) => commands.workArtifactUnlink(workItemId, artifactLinkId, options));
5937
+ const states = work.command("state").description("Manage project work states");
5938
+ states.command("list").description("List project work states").requiredOption("--project <id>", "Project id").option("--json", "Output JSON", false).action((options) => commands.workStateList(options));
5939
+ states.command("create <name>").description("Create a project work state").requiredOption("--project <id>", "Project id").requiredOption("--category <category>", "backlog, unstarted, started, completed, or canceled").option("--position <number>", "Sort position").option("--default", "Use as default state", false).option("--json", "Output JSON", false).action((name, options) => commands.workStateCreate(name, options));
5940
+ states.command("update <state-id>").description("Update a project work state").requiredOption("--project <id>", "Project id").option("--name <text>", "New name").option("--category <category>", "New lifecycle category").option("--position <number>", "New sort position").option("--default", "Use as default state", false).option("--json", "Output JSON", false).action((stateId, options) => commands.workStateUpdate(stateId, options));
5941
+ states.command("delete <state-id>").description("Delete a state, optionally migrating its work items").requiredOption("--project <id>", "Project id").option("--migrate-to <state-id>", "Destination state for existing work items").option("--json", "Output JSON", false).action((stateId, options) => commands.workStateDelete(stateId, options));
5884
5942
  }
5885
5943
  //#endregion
5886
5944
  //#region src/cli/app/register-session-commands.ts
@@ -6142,4 +6200,4 @@ program.command("usage").description("Show observed LLM usage snapshots, history
6142
6200
  //#endregion
6143
6201
  export { program as nextclawCliProgram };
6144
6202
 
6145
- //# sourceMappingURL=nextclaw-cli-app-cRvTVj04.js.map
6203
+ //# sourceMappingURL=nextclaw-cli-app-FS6IsYq6.js.map