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.
- package/NOTICES +1 -1
- package/README.md +6 -0
- package/beatrina_V0.9.43.html +1885 -0
- package/beatrina_V0.9.43.html.inputs.json +1 -0
- package/bin/beatrina.mjs +156 -26
- package/bin/browser.mjs +31 -0
- package/bin/cli.mjs +64 -5
- package/bin/doctor-rows.mjs +31 -0
- package/bin/failsafe.mjs +8 -3
- package/bin/finder.mjs +74 -0
- package/bin/lsquery.swift +55 -0
- package/bin/pages.mjs +81 -0
- package/bin/python-setup.mjs +207 -0
- package/bin/runtime-dirs.mjs +27 -0
- package/bin/sessions.mjs +30 -10
- package/bin/shortcut.mjs +271 -35
- package/bin/update-check.mjs +2 -2
- package/build-info.json +1 -1
- package/engines/js/worker.mjs +6 -3
- package/engines/python/adapter.py +14 -13
- package/engines/python/analyze.py +4 -2
- package/engines/python/bootstrap.py +32 -31
- package/engines/python/debugger.py +10 -6
- package/engines/python/engine.json +1 -1
- package/engines/python/handoff.py +4 -2
- package/engines/python/worker.py +39 -10
- package/engines/r/engine.json +1 -1
- package/engines/r/handoff.R +4 -3
- package/failsafe/ai-policy.R +16 -16
- package/failsafe/ai-store.R +6 -6
- package/failsafe/cite.R +11 -11
- package/failsafe/journal.R +11 -11
- package/failsafe/plugins.R +27 -27
- package/failsafe/serve.R +364 -239
- package/failsafe/session-documents.R +104 -0
- package/host/ai-authority.mjs +55 -0
- package/host/ai-policy.mjs +48 -26
- package/host/bundle.mjs +194 -0
- package/host/deployment.mjs +25 -18
- package/host/engine-js.mjs +16 -17
- package/host/engine-pool.mjs +56 -8
- package/host/engine-python.mjs +80 -31
- package/host/engine-r.mjs +38 -32
- package/host/engine-stdio.mjs +45 -12
- package/host/env-names.mjs +148 -0
- package/host/gateway-token.mjs +51 -0
- package/host/journal-store.mjs +10 -9
- package/host/main.mjs +216 -76
- package/host/payload.mjs +131 -0
- package/host/planes/README.md +1 -1
- package/host/planes/ai-store.mjs +6 -5
- package/host/planes/ai.mjs +49 -29
- package/host/planes/analyze.mjs +6 -5
- package/host/planes/bundle.mjs +344 -0
- package/host/planes/choose.mjs +404 -0
- package/host/planes/cite.mjs +18 -17
- package/host/planes/files.mjs +0 -0
- package/host/planes/jobs.mjs +43 -20
- package/host/planes/latex.mjs +41 -5
- package/host/planes/mcp.mjs +80 -91
- package/host/planes/pair.mjs +24 -24
- package/host/planes/pipe-term.mjs +2 -2
- package/host/planes/plugins.mjs +1 -1
- package/host/planes/recent-documents.mjs +170 -0
- package/host/planes/sessions.mjs +230 -82
- package/host/planes/settings.mjs +4 -4
- package/host/planes/terminal.mjs +13 -11
- package/host/planes/test-file.mjs +2 -2
- package/host/planes/update.mjs +6 -13
- package/host/plugin-store.mjs +35 -52
- package/host/recent-documents.mjs +124 -0
- package/host/runtime-dir.mjs +47 -0
- package/host/server.mjs +123 -21
- package/host/session-keep.mjs +70 -0
- package/host/settings.mjs +85 -37
- package/host/update-record.mjs +3 -2
- package/host/user-dirs.mjs +60 -18
- package/host/which.mjs +39 -0
- package/host/windows-runtime.mjs +6 -3
- package/host/worker-plane.mjs +80 -7
- package/host/ws.mjs +9 -2
- package/host/zip.mjs +237 -0
- package/kernel/analyze.R +1 -1
- package/{check → kernel/check}/acceptance.mjs +1 -1
- package/kernel/check/knit-file.mjs +19639 -0
- package/{check → kernel/check}/session.mjs +49 -8
- package/kernel/deployment.R +20 -20
- package/kernel/examples/NOTICE.md +1 -1
- package/kernel/fileio.R +6 -6
- package/kernel/index.html +2 -2
- package/kernel/job-run.R +83 -22
- package/kernel/jobs.R +28 -12
- package/kernel/kernel-version +1 -1
- package/kernel/kernel.R +24 -21
- package/kernel/knitr-run.R +50 -9
- package/kernel/latex.R +429 -30
- package/kernel/mcp/{carmar-mcp.mjs → beatrina-mcp.mjs} +249 -59
- package/kernel/notebook-page.R +7 -7
- package/kernel/project.R +10 -10
- package/kernel/settings.R +105 -56
- package/kernel/sniff.R +4 -4
- package/kernel/typst.R +121 -0
- package/kernel/worker.R +602 -123
- package/kernel/workspace-keep.R +188 -0
- package/lib/agent-authoring-contract.js +28 -19
- package/lib/cell-kinds.js +3 -3
- package/lib/engine-labels.js +4 -4
- package/menu/Beatrina Menu.app/Contents/Info.plist +14 -0
- package/menu/Beatrina Menu.app/Contents/MacOS/Beatrina Menu +0 -0
- package/menu/Beatrina Menu.app/Contents/_CodeSignature/CodeResources +115 -0
- package/package.json +4 -3
- 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
|
|
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/
|
|
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 = "
|
|
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
|
-
"
|
|
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
|
-
/**
|
|
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: "
|
|
315
|
-
{ re: /\bsetwd\s*\(/, what: "
|
|
316
|
-
{ re: /\brm\s*\(\s*list\s*=\s*ls\s*\(/, what: "
|
|
317
|
-
{ re: /\bunlink\s*\(|\bfile\.remove\s*\(/, what: "
|
|
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: "
|
|
336
|
-
{ re: /\bos\.chdir\s*\(/, what: "
|
|
337
|
-
{ re: /\bshutil\.rmtree\s*\(|\bos\.(?:remove|unlink|rmdir)\s*\(/, what: "
|
|
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
|
-
+ `
|
|
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}
|
|
480
|
-
+ "
|
|
481
|
-
+ "not
|
|
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}
|
|
503
|
-
+ "
|
|
504
|
-
+ "not
|
|
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 `<!--
|
|
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
|
|
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 `<!--
|
|
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
|
package/lib/engine-labels.js
CHANGED
|
@@ -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/
|
|
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/
|
|
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
|
|
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
|
-
* `
|
|
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>
|
|
Binary file
|
|
@@ -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.
|
|
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
|
-
"
|
|
25
|
+
"menu",
|
|
26
26
|
"build-info.json",
|
|
27
|
-
"
|
|
27
|
+
"beatrina_V0.9.43.html",
|
|
28
|
+
"beatrina_V0.9.43.html.inputs.json",
|
|
28
29
|
"README.md",
|
|
29
30
|
"LICENSE",
|
|
30
31
|
"NOTICES"
|