beatrina 0.8.6 → 0.9.43

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/NOTICES +1 -1
  2. package/README.md +6 -0
  3. package/beatrina_V0.9.43.html +1885 -0
  4. package/beatrina_V0.9.43.html.inputs.json +1 -0
  5. package/bin/beatrina.mjs +156 -26
  6. package/bin/browser.mjs +31 -0
  7. package/bin/cli.mjs +64 -5
  8. package/bin/doctor-rows.mjs +31 -0
  9. package/bin/failsafe.mjs +8 -3
  10. package/bin/finder.mjs +74 -0
  11. package/bin/lsquery.swift +55 -0
  12. package/bin/pages.mjs +81 -0
  13. package/bin/python-setup.mjs +207 -0
  14. package/bin/runtime-dirs.mjs +27 -0
  15. package/bin/sessions.mjs +30 -10
  16. package/bin/shortcut.mjs +271 -35
  17. package/bin/update-check.mjs +2 -2
  18. package/build-info.json +1 -1
  19. package/engines/js/worker.mjs +6 -3
  20. package/engines/python/adapter.py +14 -13
  21. package/engines/python/analyze.py +4 -2
  22. package/engines/python/bootstrap.py +32 -31
  23. package/engines/python/debugger.py +10 -6
  24. package/engines/python/engine.json +1 -1
  25. package/engines/python/handoff.py +4 -2
  26. package/engines/python/worker.py +39 -10
  27. package/engines/r/engine.json +1 -1
  28. package/engines/r/handoff.R +4 -3
  29. package/failsafe/ai-policy.R +16 -16
  30. package/failsafe/ai-store.R +6 -6
  31. package/failsafe/cite.R +11 -11
  32. package/failsafe/journal.R +11 -11
  33. package/failsafe/plugins.R +27 -27
  34. package/failsafe/serve.R +364 -239
  35. package/failsafe/session-documents.R +104 -0
  36. package/host/ai-authority.mjs +55 -0
  37. package/host/ai-policy.mjs +48 -26
  38. package/host/bundle.mjs +194 -0
  39. package/host/deployment.mjs +25 -18
  40. package/host/engine-js.mjs +16 -17
  41. package/host/engine-pool.mjs +56 -8
  42. package/host/engine-python.mjs +80 -31
  43. package/host/engine-r.mjs +38 -32
  44. package/host/engine-stdio.mjs +45 -12
  45. package/host/env-names.mjs +148 -0
  46. package/host/gateway-token.mjs +51 -0
  47. package/host/journal-store.mjs +10 -9
  48. package/host/main.mjs +216 -76
  49. package/host/payload.mjs +131 -0
  50. package/host/planes/README.md +1 -1
  51. package/host/planes/ai-store.mjs +6 -5
  52. package/host/planes/ai.mjs +49 -29
  53. package/host/planes/analyze.mjs +6 -5
  54. package/host/planes/bundle.mjs +344 -0
  55. package/host/planes/choose.mjs +404 -0
  56. package/host/planes/cite.mjs +18 -17
  57. package/host/planes/files.mjs +0 -0
  58. package/host/planes/jobs.mjs +43 -20
  59. package/host/planes/latex.mjs +41 -5
  60. package/host/planes/mcp.mjs +80 -91
  61. package/host/planes/pair.mjs +24 -24
  62. package/host/planes/pipe-term.mjs +2 -2
  63. package/host/planes/plugins.mjs +1 -1
  64. package/host/planes/recent-documents.mjs +170 -0
  65. package/host/planes/sessions.mjs +230 -82
  66. package/host/planes/settings.mjs +4 -4
  67. package/host/planes/terminal.mjs +13 -11
  68. package/host/planes/test-file.mjs +2 -2
  69. package/host/planes/update.mjs +6 -13
  70. package/host/plugin-store.mjs +35 -52
  71. package/host/recent-documents.mjs +124 -0
  72. package/host/runtime-dir.mjs +47 -0
  73. package/host/server.mjs +123 -21
  74. package/host/session-keep.mjs +70 -0
  75. package/host/settings.mjs +85 -37
  76. package/host/update-record.mjs +3 -2
  77. package/host/user-dirs.mjs +60 -18
  78. package/host/which.mjs +39 -0
  79. package/host/windows-runtime.mjs +6 -3
  80. package/host/worker-plane.mjs +80 -7
  81. package/host/ws.mjs +9 -2
  82. package/host/zip.mjs +237 -0
  83. package/kernel/analyze.R +1 -1
  84. package/{check → kernel/check}/acceptance.mjs +1 -1
  85. package/kernel/check/knit-file.mjs +19639 -0
  86. package/{check → kernel/check}/session.mjs +49 -8
  87. package/kernel/deployment.R +20 -20
  88. package/kernel/examples/NOTICE.md +1 -1
  89. package/kernel/fileio.R +6 -6
  90. package/kernel/index.html +2 -2
  91. package/kernel/job-run.R +83 -22
  92. package/kernel/jobs.R +28 -12
  93. package/kernel/kernel-version +1 -1
  94. package/kernel/kernel.R +24 -21
  95. package/kernel/knitr-run.R +50 -9
  96. package/kernel/latex.R +429 -30
  97. package/kernel/mcp/{carmar-mcp.mjs → beatrina-mcp.mjs} +249 -59
  98. package/kernel/notebook-page.R +7 -7
  99. package/kernel/project.R +10 -10
  100. package/kernel/settings.R +105 -56
  101. package/kernel/sniff.R +4 -4
  102. package/kernel/typst.R +121 -0
  103. package/kernel/worker.R +602 -123
  104. package/kernel/workspace-keep.R +188 -0
  105. package/lib/agent-authoring-contract.js +28 -19
  106. package/lib/cell-kinds.js +3 -3
  107. package/lib/engine-labels.js +4 -4
  108. package/menu/Beatrina Menu.app/Contents/Info.plist +14 -0
  109. package/menu/Beatrina Menu.app/Contents/MacOS/Beatrina Menu +0 -0
  110. package/menu/Beatrina Menu.app/Contents/_CodeSignature/CodeResources +115 -0
  111. package/package.json +4 -3
  112. package/carmar_V0.8.6.html +0 -1310
@@ -0,0 +1,188 @@
1
+ # workspace-keep.R — "Restart, keep variables": the global environment carried
2
+ # across a restart, the way RStudio carries it.
3
+ #
4
+ # Sourced into the WORKER's private scope (worker.R's import_sources), and
5
+ # testable without a worker: spike/test-workspace-keep.R sources this file
6
+ # alone.
7
+ #
8
+ # This replaces a removed design, and the difference is the point. Until 7.59
9
+ # a restart ran save.image() inside a handoff with a deadline and load()ed one
10
+ # file back; large sessions missed the deadline, one unreadable object sank the
11
+ # whole file, and nothing said what was lost. The owner removed it ("restore
12
+ # work has been abysmally bad"). What is here instead:
13
+ #
14
+ # * NO DEADLINE. The save is a worker command like any other: progress frames
15
+ # while it runs, Interrupt (Stop) cancels it, and the restart only happens
16
+ # once the save has answered.
17
+ # * ONE FILE PER OBJECT. An object that fails to save or to load is that
18
+ # object, named in the report — never the whole workspace.
19
+ # * PACKAGES ARE NOT RECORDED. A notebook's library() calls live in its setup
20
+ # chunk, and the page runs setup before the first chunk of a fresh R
21
+ # (lib/document-execution.js) — RStudio's own answer, and the reason this
22
+ # file keeps variables only.
23
+ # * WHAT CANNOT TRAVEL IS SAID. A connection or an external pointer
24
+ # serialises to a dead handle; it is skipped at save time and listed.
25
+ #
26
+ # Known limit, stated rather than hidden: objects are saved separately, so two
27
+ # objects that SHARE an environment (R6 objects pointing at one another) come
28
+ # back as copies that no longer share it.
29
+ #
30
+ # The token names a directory under the keep root and is the only thing that
31
+ # travels: the page and the supervisor never pass a path, so a resume can only
32
+ # ever read a directory this code wrote.
33
+
34
+ #' Where kept workspaces live: 0700, per user. `BEATRINA_KEEP_DIR` relocates it
35
+ #' for tests.
36
+ #' @return A directory path (not created).
37
+ keep_root <- function() {
38
+ env <- Sys.getenv("BEATRINA_KEEP_DIR", "")
39
+ if (nzchar(env)) env else file.path(tools::R_user_dir("beatrina", "data"), "kept-workspace")
40
+ }
41
+
42
+ KEEP_TOKEN_RE <- "^[0-9]{14}-[0-9a-f]{16}$"
43
+
44
+ #' Is `x` a token this file could have minted?
45
+ #' @param x Anything the wire delivered.
46
+ #' @return TRUE for one well-formed token.
47
+ keep_token_ok <- function(x) {
48
+ is.character(x) && length(x) == 1L && !is.na(x) && grepl(KEEP_TOKEN_RE, x)
49
+ }
50
+
51
+ #' A fresh token: a timestamp (sorts, and ages out) plus 64 random bits.
52
+ #' @return A token matching KEEP_TOKEN_RE.
53
+ keep_token_new <- function() {
54
+ bytes <- tryCatch({
55
+ con <- file("/dev/urandom", "rb", raw = TRUE)
56
+ on.exit(close(con), add = TRUE)
57
+ readBin(con, "raw", n = 8L)
58
+ }, error = function(e) as.raw(sample.int(256L, 8L, replace = TRUE) - 1L))
59
+ paste0(format(Sys.time(), "%Y%m%d%H%M%S"), "-", paste(format(bytes), collapse = ""))
60
+ }
61
+
62
+ #' Why an object cannot travel, or NULL when it can.
63
+ #'
64
+ #' A connection is an integer index into THIS process's connection table, and
65
+ #' an external pointer is an address in this process's memory: both serialise
66
+ #' without complaint and come back as something that looks right and is dead.
67
+ #' @param x The object.
68
+ #' @return NULL, or a short phrase for the report.
69
+ keep_unsaveable <- function(x) {
70
+ if (inherits(x, "connection")) return("a connection (a file or URL handle)")
71
+ if (inherits(x, "DBIConnection")) return("a database connection")
72
+ if (typeof(x) == "externalptr") return("an external pointer")
73
+ NULL
74
+ }
75
+
76
+ #' Delete kept workspaces older than `max_age_s` — a restart that never
77
+ #' resumed (a crash, a closed laptop) must not keep a copy of someone's data
78
+ #' forever.
79
+ #' @param root The keep root.
80
+ #' @param max_age_s Age in seconds.
81
+ #' @return Invisibly, the tokens removed.
82
+ keep_prune <- function(root = keep_root(), max_age_s = 86400) {
83
+ if (!dir.exists(root)) return(invisible(character(0)))
84
+ tokens <- Filter(keep_token_ok, list.files(root))
85
+ old <- Filter(function(t) {
86
+ age <- as.numeric(difftime(Sys.time(), file.info(file.path(root, t))$mtime, units = "secs"))
87
+ isTRUE(age > max_age_s)
88
+ }, tokens)
89
+ invisible(vapply(old, function(t) { unlink(file.path(root, t), recursive = TRUE); t }, character(1)))
90
+ }
91
+
92
+ #' Save every object of `env` into a new kept workspace.
93
+ #'
94
+ #' @param env The environment to keep (the worker passes globalenv()).
95
+ #' @param progress function(done, total, bytes, name) called after each object.
96
+ #' @param root The keep root.
97
+ #' @return list(token, saved, bytes, skipped = list of {name, why}).
98
+ #' An interrupt removes the partial directory and propagates, so the caller
99
+ #' reports "cancelled" and nothing half-written is ever resumed.
100
+ keep_save <- function(env = globalenv(), progress = function(...) NULL, root = keep_root()) {
101
+ stopifnot("`env` must be an environment" = is.environment(env))
102
+ dir.create(root, recursive = TRUE, showWarnings = FALSE, mode = "0700")
103
+ Sys.chmod(root, mode = "0700")
104
+ keep_prune(root)
105
+ token <- keep_token_new()
106
+ dir <- file.path(root, token)
107
+ dir.create(dir, mode = "0700")
108
+ names_ <- sort(ls(env, all.names = TRUE))
109
+ total <- length(names_)
110
+ bytes <- 0
111
+ done <- FALSE
112
+ on.exit(if (!done) unlink(dir, recursive = TRUE), add = TRUE)
113
+ # Objects are indexed, never named, on disk: an object name may hold any
114
+ # character, and a file name must not.
115
+ entries <- lapply(seq_along(names_), function(i) {
116
+ name <- names_[[i]]
117
+ value <- get(name, envir = env, inherits = FALSE)
118
+ why <- keep_unsaveable(value)
119
+ entry <- if (!is.null(why)) list(name = name, status = "skipped", why = why)
120
+ else {
121
+ file <- sprintf("%06d.rds", i)
122
+ failed <- tryCatch({ saveRDS(value, file.path(dir, file), compress = FALSE); NULL },
123
+ error = function(e) conditionMessage(e))
124
+ if (is.null(failed)) {
125
+ bytes <<- bytes + file.size(file.path(dir, file))
126
+ list(name = name, status = "saved", file = file, class = class(value)[1L])
127
+ } else {
128
+ unlink(file.path(dir, file))
129
+ list(name = name, status = "skipped", why = paste("could not be saved:", failed))
130
+ }
131
+ }
132
+ progress(i, total, bytes, name)
133
+ entry
134
+ })
135
+ manifest <- list(version = 1L, created = format(Sys.time(), "%Y-%m-%dT%H:%M:%S"),
136
+ wd = getwd(), entries = entries)
137
+ writeLines(jsonlite::toJSON(manifest, auto_unbox = TRUE), file.path(dir, "manifest.json"))
138
+ Sys.chmod(list.files(dir, full.names = TRUE), mode = "0600")
139
+ done <- TRUE
140
+ status <- vapply(entries, `[[`, "", "status")
141
+ list(token = token, saved = sum(status == "saved"), bytes = bytes,
142
+ skipped = lapply(entries[status == "skipped"], function(e) list(name = e$name, why = e$why)))
143
+ }
144
+
145
+ #' Load a kept workspace into `env`, then delete it.
146
+ #'
147
+ #' Each object is read on its own; one that fails is reported and the rest
148
+ #' still arrive. The directory is removed afterwards whatever happened — a
149
+ #' kept workspace is used once.
150
+ #' @param token A token from keep_save().
151
+ #' @param env Where the objects go (the worker passes globalenv()).
152
+ #' @param progress function(done, total, bytes, name) called after each object
153
+ #' (the same shape keep_save() calls, so one callback serves both; bytes is 0).
154
+ #' @param root The keep root.
155
+ #' @return list(restored, failed = list of {name, why}, skipped = the save's
156
+ #' skips, carried so the one report says everything), or list(error).
157
+ keep_resume <- function(token, env = globalenv(), progress = function(...) NULL, root = keep_root()) {
158
+ if (!keep_token_ok(token)) return(list(error = "That is not a kept workspace."))
159
+ dir <- file.path(root, token)
160
+ manifest_file <- file.path(dir, "manifest.json")
161
+ if (!file.exists(manifest_file)) {
162
+ return(list(error = "The kept workspace is gone — it was already restored or removed."))
163
+ }
164
+ on.exit(unlink(dir, recursive = TRUE), add = TRUE)
165
+ manifest <- tryCatch(jsonlite::fromJSON(manifest_file, simplifyVector = FALSE),
166
+ error = function(e) NULL)
167
+ if (is.null(manifest) || !is.list(manifest$entries)) {
168
+ return(list(error = "The kept workspace's record could not be read."))
169
+ }
170
+ saved <- Filter(function(e) identical(e$status, "saved"), manifest$entries)
171
+ total <- length(saved)
172
+ outcomes <- lapply(seq_along(saved), function(i) {
173
+ e <- saved[[i]]
174
+ # The file name is re-checked, never trusted: only NNNNNN.rds inside dir.
175
+ why <- if (!is.character(e$file) || !grepl("^[0-9]{6}\\.rds$", e$file)) "its record is malformed"
176
+ else tryCatch({
177
+ assign(e$name, readRDS(file.path(dir, e$file)), envir = env)
178
+ NULL
179
+ }, error = function(err) conditionMessage(err))
180
+ progress(i, total, 0, e$name)
181
+ if (is.null(why)) NULL else list(name = e$name, why = paste("could not be restored:", why))
182
+ })
183
+ failed <- Filter(Negate(is.null), outcomes)
184
+ skipped <- lapply(Filter(function(e) identical(e$status, "skipped"), manifest$entries),
185
+ function(e) list(name = e$name, why = e$why))
186
+ list(restored = total - length(failed), failed = failed, skipped = skipped,
187
+ wd = if (is.character(manifest$wd)) manifest$wd else "")
188
+ }
@@ -1,5 +1,5 @@
1
1
  // agent-authoring-contract.js — the one place that says how a model may write
2
- // into a CarmaR document, and the only one that decides whether it did.
2
+ // into a Beatrina document, and the only one that decides whether it did.
3
3
  //
4
4
  // WHY THIS IS A MODULE AND NOT A PROMPT. The rules below were previously
5
5
  // stated in three unsynchronised places — the MCP server's initialize text,
@@ -10,13 +10,13 @@
10
10
  // whether a proposal is valid. Only the third one is enforcement.
11
11
  //
12
12
  // Nothing here touches the DOM, the notebook, or the network, because
13
- // `tools/mcp/carmar-mcp.mjs` imports it in Node (outside the browser bundle)
13
+ // `tools/mcp/beatrina-mcp.mjs` imports it in Node (outside the browser bundle)
14
14
  // and `lib/mcp-bridge.js` imports it in the page. One module, both sides, so
15
15
  // the rule an agent is told and the rule it is held to cannot drift apart.
16
16
 
17
17
  import { CODE_KIND, isCodeKind } from "./cell-kinds.js";
18
18
 
19
- export const CONTRACT = "carmar.authoring/v1";
19
+ export const CONTRACT = "beatrina.authoring/v1";
20
20
  export const SCHEMA_VERSION = 1;
21
21
 
22
22
  /** A browser answer may still be reviewed as one bounded user-requested set. */
@@ -36,7 +36,7 @@ export const AUTHORING_RULES = [
36
36
  // document unit at a time. The removed batch tool changed the model's unit
37
37
  // of composition from "this notebook block" to "my reply, split into an
38
38
  // array", which is how chat scaffolding reached saved documents.
39
- "CarmaR is the user's live R notebook. Start with notebook_read. Build the analysis ONE "
39
+ "Beatrina is the user's live R notebook. Start with notebook_read. Build the analysis ONE "
40
40
  + "document block at a time: call chunk_insert once for each finished prose section or runnable "
41
41
  + "R step, in reading and execution order. AUTHOR every text block as part of the saved .qmd, "
42
42
  + "not as a chat reply. Never paste or divide your chat response into notebook blocks. Keep "
@@ -309,12 +309,19 @@ const PASTED_VALUE = /^\s*\[\d+\]\s+\S/m;
309
309
  // a comment, so it is untouched.
310
310
  const CONSOLE_TRANSCRIPT = /^\s*(>\s+\S|\[\d+\]\s+\S)/m;
311
311
 
312
- /** Side effects on the user's machine that a chunk should not quietly carry. */
312
+ /**
313
+ * Side effects on the user's machine that a chunk should not quietly carry.
314
+ *
315
+ * Worded as what the code WOULD do when it runs: this is a reading of the
316
+ * proposed source, made before anything executes, and an agent that saw
317
+ * "changes the working directory" beside a run that was restored from the
318
+ * cache took it for a report of what had happened.
319
+ */
313
320
  const SIDE_EFFECTS = [
314
- { re: /\binstall\.packages\s*\(/, what: "installs packages into the user's R library" },
315
- { re: /\bsetwd\s*\(/, what: "changes the working directory for the whole session" },
316
- { re: /\brm\s*\(\s*list\s*=\s*ls\s*\(/, what: "erases every object in the user's session" },
317
- { re: /\bunlink\s*\(|\bfile\.remove\s*\(/, what: "deletes files from disk" },
321
+ { re: /\binstall\.packages\s*\(/, what: "would install packages into the user's R library" },
322
+ { re: /\bsetwd\s*\(/, what: "would change the working directory for the whole session" },
323
+ { re: /\brm\s*\(\s*list\s*=\s*ls\s*\(/, what: "would erase every object in the user's session" },
324
+ { re: /\bunlink\s*\(|\bfile\.remove\s*\(/, what: "would delete files from disk" },
318
325
  ];
319
326
 
320
327
  // A path out of the project, in a string. `~` alone is not checked: in R it is
@@ -332,9 +339,9 @@ const ENGINE_RULES = {
332
339
  transcript: /^\s*>>>\s/m,
333
340
  transcriptWhat: "a Python console TRANSCRIPT — lines beginning `>>> `",
334
341
  sideEffects: [
335
- { re: /(^|\n)\s*[!%]\s*pip\s+install\b|\bpip\.main\s*\(|["']pip["']\s*,\s*["']install["']/, what: "installs packages into the user's Python environment" },
336
- { re: /\bos\.chdir\s*\(/, what: "changes the working directory for the whole session" },
337
- { re: /\bshutil\.rmtree\s*\(|\bos\.(?:remove|unlink|rmdir)\s*\(/, what: "deletes files from disk" },
342
+ { re: /(^|\n)\s*[!%]\s*pip\s+install\b|\bpip\.main\s*\(|["']pip["']\s*,\s*["']install["']/, what: "would install packages into the user's Python environment" },
343
+ { re: /\bos\.chdir\s*\(/, what: "would change the working directory for the whole session" },
344
+ { re: /\bshutil\.rmtree\s*\(|\bos\.(?:remove|unlink|rmdir)\s*\(/, what: "would delete files from disk" },
338
345
  ],
339
346
  },
340
347
  };
@@ -389,7 +396,7 @@ export function normalizeBlocks(raw, { tool = "This answer", max = MAX_BLOCKS, e
389
396
  const isText = declared === "text" || declared === "md";
390
397
  if (!isText && declared !== "r" && !isCodeKind(declared)) {
391
398
  throw repair(`${ordinal(index)} has kind "${declared}". `
392
- + `CarmaR blocks are ${BLOCK_KINDS.map((k) => `"${k}"`).join(" or ")}.`);
399
+ + `Beatrina blocks are ${BLOCK_KINDS.map((k) => `"${k}"`).join(" or ")}.`);
393
400
  }
394
401
  const content = String(item && item.content != null ? item.content : "");
395
402
  if (!content.trim()) {
@@ -476,9 +483,10 @@ export function normalizeBlocks(raw, { tool = "This answer", max = MAX_BLOCKS, e
476
483
  }
477
484
  rules.sideEffects.forEach(({ re, what }) => {
478
485
  if (re.test(content)) {
479
- lints.push(`${ordinal(index)} ${what}. The user reviews this block before it is `
480
- + "theirs, but do not include it unless they asked for it — a chunk should compute, "
481
- + "not change the machine it runs on.");
486
+ lints.push(`${ordinal(index)} ${what} when it runs — a note on the code as written, `
487
+ + "not a report that it ran. The user reviews this block before it is theirs, but do "
488
+ + "not include it unless they asked for it: a chunk should compute, not change the "
489
+ + "machine it runs on.");
482
490
  }
483
491
  });
484
492
  if (ABSOLUTE_PATH.test(content)) {
@@ -499,9 +507,10 @@ export function normalizeBlocks(raw, { tool = "This answer", max = MAX_BLOCKS, e
499
507
  }
500
508
  SIDE_EFFECTS.forEach(({ re, what }) => {
501
509
  if (re.test(content)) {
502
- lints.push(`${ordinal(index)} ${what}. The user reviews this block before it is `
503
- + "theirs, but do not include it unless they asked for it — a chunk should compute, "
504
- + "not change the machine it runs on.");
510
+ lints.push(`${ordinal(index)} ${what} when it runs — a note on the code as written, `
511
+ + "not a report that it ran. The user reviews this block before it is theirs, but do "
512
+ + "not include it unless they asked for it: a chunk should compute, not change the "
513
+ + "machine it runs on.");
505
514
  }
506
515
  });
507
516
  if (ABSOLUTE_PATH.test(content)) {
package/lib/cell-kinds.js CHANGED
@@ -17,10 +17,10 @@
17
17
  // a saved .html. Still ACCEPTED everywhere,
18
18
  // and still registered as a (hidden) verb so
19
19
  // such a cell mounts. Never emitted.
20
- // disk `rcode` · `md` the `kind` field of a `<!-- carmar-cell:`
20
+ // disk `rcode` · `md` the `kind` field of a `<!-- beatrina-cell:`
21
21
  // annotation in a .carmd/.qmd. THE DOCUMENT
22
22
  // FORMAT, and deliberately unchanged: a file
23
- // written today must still open in CarmaR
23
+ // written today must still open in Beatrina
24
24
  // 0.8.01, whose reader validates exactly
25
25
  // `['rcode','md']`. lib/document-source-cells.js
26
26
  // translates at that one boundary and
@@ -82,7 +82,7 @@ export function cellKind(kind) {
82
82
  // ── the document-format boundary ────────────────────────────────────────────
83
83
 
84
84
  /**
85
- * The `kind` to WRITE into a `<!-- carmar-cell: … -->` annotation.
85
+ * The `kind` to WRITE into a `<!-- beatrina-cell: … -->` annotation.
86
86
  *
87
87
  * A code chunk is written as `rcode` whatever this build calls it, because
88
88
  * that is the format, and a file CarmaR 0.8.01 refuses to open is a file its
@@ -245,10 +245,10 @@ export function declaredEngines(info) {
245
245
  }
246
246
 
247
247
  // ── authoring: what an AI may say about a chunk's language ─────────────────
248
- // The tool schemas (lib/ai-tools.js, lib/mcp-bridge.js, tools/mcp/carmar-mcp.mjs)
248
+ // The tool schemas (lib/ai-tools.js, lib/mcp-bridge.js, tools/mcp/beatrina-mcp.mjs)
249
249
  // all name engines from HERE, so the enum a model is shown, the refusal it gets
250
250
  // and the header a chunk is written with cannot disagree. Pure and import-free:
251
- // tools/mcp/carmar-mcp.mjs loads it in Node beside the authoring contract.
251
+ // tools/mcp/beatrina-mcp.mjs loads it in Node beside the authoring contract.
252
252
 
253
253
  /** The engines a tool schema offers. The SESSION decides which of them run. */
254
254
  export const AUTHORING_ENGINES = Object.freeze(KNOWN_ENGINES.map((e) => e.key));
@@ -267,14 +267,14 @@ export function engineArg(value, name = "engine") {
267
267
  }
268
268
  const key = engineKey(value);
269
269
  if (!AUTHORING_ENGINES.includes(key)) {
270
- throw new Error(`${name} "${value}" is not an engine CarmaR knows. Use one of ${AUTHORING_ENGINES.join(", ")}.`);
270
+ throw new Error(`${name} "${value}" is not an engine Beatrina knows. Use one of ${AUTHORING_ENGINES.join(", ")}.`);
271
271
  }
272
272
  return key;
273
273
  }
274
274
 
275
275
  /**
276
276
  * The chunk header a new code chunk carries for `engine` — the SAME shape the
277
- * `carmar:insert-cell` door writes (src/notebook-entry.js). R gets none, so an
277
+ * `beatrina:insert-cell` door writes (src/notebook-entry.js). R gets none, so an
278
278
  * R insert is byte-identical to one made before engines existed.
279
279
  *
280
280
  * @returns {object|null}
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0"><dict>
4
+ <key>CFBundleName</key><string>Beatrina Menu</string>
5
+ <key>CFBundleDisplayName</key><string>Beatrina Menu</string>
6
+ <key>CFBundleExecutable</key><string>Beatrina Menu</string>
7
+ <key>CFBundleIdentifier</key><string>me.saqr.beatrina.menu</string>
8
+ <key>CFBundlePackageType</key><string>APPL</string>
9
+ <key>CFBundleShortVersionString</key><string>0.9.43</string>
10
+ <key>CFBundleVersion</key><string>0.9.43</string>
11
+ <key>LSMinimumSystemVersion</key><string>13.0</string>
12
+ <!-- Menu bar only: no Dock tile, no window, nothing to close by accident. -->
13
+ <key>LSUIElement</key><true/>
14
+ </dict></plist>
@@ -0,0 +1,115 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>files</key>
6
+ <dict/>
7
+ <key>files2</key>
8
+ <dict/>
9
+ <key>rules</key>
10
+ <dict>
11
+ <key>^Resources/</key>
12
+ <true/>
13
+ <key>^Resources/.*\.lproj/</key>
14
+ <dict>
15
+ <key>optional</key>
16
+ <true/>
17
+ <key>weight</key>
18
+ <real>1000</real>
19
+ </dict>
20
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
21
+ <dict>
22
+ <key>omit</key>
23
+ <true/>
24
+ <key>weight</key>
25
+ <real>1100</real>
26
+ </dict>
27
+ <key>^Resources/Base\.lproj/</key>
28
+ <dict>
29
+ <key>weight</key>
30
+ <real>1010</real>
31
+ </dict>
32
+ <key>^version.plist$</key>
33
+ <true/>
34
+ </dict>
35
+ <key>rules2</key>
36
+ <dict>
37
+ <key>.*\.dSYM($|/)</key>
38
+ <dict>
39
+ <key>weight</key>
40
+ <real>11</real>
41
+ </dict>
42
+ <key>^(.*/)?\.DS_Store$</key>
43
+ <dict>
44
+ <key>omit</key>
45
+ <true/>
46
+ <key>weight</key>
47
+ <real>2000</real>
48
+ </dict>
49
+ <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
50
+ <dict>
51
+ <key>nested</key>
52
+ <true/>
53
+ <key>weight</key>
54
+ <real>10</real>
55
+ </dict>
56
+ <key>^.*</key>
57
+ <true/>
58
+ <key>^Info\.plist$</key>
59
+ <dict>
60
+ <key>omit</key>
61
+ <true/>
62
+ <key>weight</key>
63
+ <real>20</real>
64
+ </dict>
65
+ <key>^PkgInfo$</key>
66
+ <dict>
67
+ <key>omit</key>
68
+ <true/>
69
+ <key>weight</key>
70
+ <real>20</real>
71
+ </dict>
72
+ <key>^Resources/</key>
73
+ <dict>
74
+ <key>weight</key>
75
+ <real>20</real>
76
+ </dict>
77
+ <key>^Resources/.*\.lproj/</key>
78
+ <dict>
79
+ <key>optional</key>
80
+ <true/>
81
+ <key>weight</key>
82
+ <real>1000</real>
83
+ </dict>
84
+ <key>^Resources/.*\.lproj/locversion.plist$</key>
85
+ <dict>
86
+ <key>omit</key>
87
+ <true/>
88
+ <key>weight</key>
89
+ <real>1100</real>
90
+ </dict>
91
+ <key>^Resources/Base\.lproj/</key>
92
+ <dict>
93
+ <key>weight</key>
94
+ <real>1010</real>
95
+ </dict>
96
+ <key>^[^/]+$</key>
97
+ <dict>
98
+ <key>nested</key>
99
+ <true/>
100
+ <key>weight</key>
101
+ <real>10</real>
102
+ </dict>
103
+ <key>^embedded\.provisionprofile$</key>
104
+ <dict>
105
+ <key>weight</key>
106
+ <real>20</real>
107
+ </dict>
108
+ <key>^version\.plist$</key>
109
+ <dict>
110
+ <key>weight</key>
111
+ <real>20</real>
112
+ </dict>
113
+ </dict>
114
+ </dict>
115
+ </plist>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beatrina",
3
- "version": "0.8.6",
3
+ "version": "0.9.43",
4
4
  "description": "Beatrina is a modern, powerful, feature-rich integrated development environment (IDE) for R, Python, and JavaScript.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,9 +22,10 @@
22
22
  "kernel",
23
23
  "failsafe",
24
24
  "lib",
25
- "check",
25
+ "menu",
26
26
  "build-info.json",
27
- "carmar_V0.8.6.html",
27
+ "beatrina_V0.9.43.html",
28
+ "beatrina_V0.9.43.html.inputs.json",
28
29
  "README.md",
29
30
  "LICENSE",
30
31
  "NOTICES"