@volter/supercode-teams 0.2.69 → 0.2.71

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-teams",
3
- "version": "0.2.69",
3
+ "version": "0.2.71",
4
4
  "type": "module",
5
5
  "description": "A machine daemon serving this machine's doors, and a local or remote Teams server for shared session discovery, OpenTelemetry ingestion and access to enrolled machines.",
6
6
  "license": "MIT",
@@ -8,6 +8,7 @@ import { chmodSync, existsSync, writeFileSync } from 'node:fs';
8
8
  import { tmpdir } from 'node:os';
9
9
  import { join } from 'node:path';
10
10
  import { randomBytes } from 'node:crypto';
11
+ import { weztermConfigFile } from './terminal-look.mjs';
11
12
 
12
13
  const onPath = (name) => {
13
14
  const found = spawnSync(process.platform === 'win32' ? 'where' : 'which', [name], { encoding: 'utf8', timeout: 3_000 });
@@ -29,7 +30,8 @@ function selfDeletingScript(argv, extension) {
29
30
  /** Adapters by platform, in rank order: { name, find() → program or null, launch(program, argv) → [command, args] }. */
30
31
  const ADAPTERS = {
31
32
  darwin: [
32
- { name: 'wezterm', find: () => onPath('wezterm') ?? firstPath('/Applications/WezTerm.app/Contents/MacOS/wezterm'), launch: (p, argv) => [p, ['start', '--always-new-process', '--', ...argv]] },
33
+ // WezTerm opens with supercode's own config: it looks like this machine's Terminal.app (terminal-look.mjs).
34
+ { name: 'wezterm', find: () => onPath('wezterm') ?? firstPath('/Applications/WezTerm.app/Contents/MacOS/wezterm'), launch: (p, argv) => [p, ['--config-file', weztermConfigFile(), 'start', '--always-new-process', '--', ...argv]] },
33
35
  { name: 'kitty', find: () => onPath('kitty') ?? firstPath('/Applications/kitty.app/Contents/MacOS/kitty'), launch: (p, argv) => [p, argv] },
34
36
  { name: 'ghostty', find: () => firstPath('/Applications/Ghostty.app'), launch: (p, argv) => ['open', ['-na', p, '--args', '-e', ...argv]] },
35
37
  { name: 'alacritty', find: () => onPath('alacritty') ?? firstPath('/Applications/Alacritty.app/Contents/MacOS/alacritty'), launch: (p, argv) => [p, ['-e', ...argv]] },
@@ -41,13 +43,13 @@ const ADAPTERS = {
41
43
  // Windows Terminal first: its window opens in front of the others, where one started by a background process
42
44
  // (the daemon is one) opens behind them; WezTerm started that way never painted its window (volter-desktop).
43
45
  { name: 'windows-terminal', find: () => onPath('wt') ?? firstPath(join(localApps, 'Microsoft', 'WindowsApps', 'wt.exe')), launch: (p, argv) => [p, ['-w', 'new', 'new-tab', '--', ...argv]] },
44
- { name: 'wezterm', find: () => onPath('wezterm') ?? firstPath(join(programFiles, 'WezTerm', 'wezterm.exe')), launch: (p, argv) => [p, ['start', '--always-new-process', '--', ...argv]] },
46
+ { name: 'wezterm', find: () => onPath('wezterm') ?? firstPath(join(programFiles, 'WezTerm', 'wezterm.exe')), launch: (p, argv) => [p, ['--config-file', weztermConfigFile(), 'start', '--always-new-process', '--', ...argv]] },
45
47
  { name: 'alacritty', find: () => onPath('alacritty') ?? firstPath(join(programFiles, 'Alacritty', 'alacritty.exe')), launch: (p, argv) => [p, ['-e', ...argv]] },
46
48
  // A console window of its own, on every Windows: it lives exactly as long as its command.
47
49
  { name: 'conhost', find: () => firstPath(join(process.env.SystemRoot ?? 'C:\\Windows', 'System32', 'conhost.exe')), launch: (p, argv) => [p, argv] },
48
50
  ],
49
51
  linux: [
50
- { name: 'wezterm', find: () => onPath('wezterm'), launch: (p, argv) => [p, ['start', '--always-new-process', '--', ...argv]] },
52
+ { name: 'wezterm', find: () => onPath('wezterm'), launch: (p, argv) => [p, ['--config-file', weztermConfigFile(), 'start', '--always-new-process', '--', ...argv]] },
51
53
  { name: 'kitty', find: () => onPath('kitty'), launch: (p, argv) => [p, argv] },
52
54
  { name: 'ghostty', find: () => onPath('ghostty'), launch: (p, argv) => [p, ['-e', ...argv]] },
53
55
  { name: 'alacritty', find: () => onPath('alacritty'), launch: (p, argv) => [p, ['-e', ...argv]] },
@@ -0,0 +1,49 @@
1
+ // Generated by sdk/teams/scripts/terminal-basic.mjs from https://brand.volter.ai/tokens.json
2
+ // (the brand's terminal roles: Terminal.app's Basic profile). Do not edit; re-run the script after a brand change.
3
+ export const TERMINAL_BASIC = {
4
+ "light": {
5
+ "background": "#ffffff",
6
+ "foreground": "#000000",
7
+ "cursor": "#9d9d9d",
8
+ "selection": "#b3d7ff",
9
+ "black": "#000000",
10
+ "red": "#990000",
11
+ "green": "#00a600",
12
+ "yellow": "#999900",
13
+ "blue": "#0000b3",
14
+ "magenta": "#b300b3",
15
+ "cyan": "#00a6b3",
16
+ "white": "#bfbfbf",
17
+ "brightBlack": "#666666",
18
+ "brightRed": "#e60000",
19
+ "brightGreen": "#00d900",
20
+ "brightYellow": "#e6e600",
21
+ "brightBlue": "#0000ff",
22
+ "brightMagenta": "#e600e6",
23
+ "brightCyan": "#00e6e6",
24
+ "brightWhite": "#e6e6e6"
25
+ },
26
+ "dark": {
27
+ "background": "#1e1e1e",
28
+ "foreground": "#ffffff",
29
+ "cursor": "#9d9d9d",
30
+ "selection": "#3f638b",
31
+ "black": "#000000",
32
+ "red": "#990000",
33
+ "green": "#00a600",
34
+ "yellow": "#999900",
35
+ "blue": "#0000b3",
36
+ "magenta": "#b300b3",
37
+ "cyan": "#00a6b3",
38
+ "white": "#bfbfbf",
39
+ "brightBlack": "#666666",
40
+ "brightRed": "#e60000",
41
+ "brightGreen": "#00d900",
42
+ "brightYellow": "#e6e600",
43
+ "brightBlue": "#0000ff",
44
+ "brightMagenta": "#e600e6",
45
+ "brightCyan": "#00e6e6",
46
+ "brightWhite": "#e6e6e6"
47
+ },
48
+ "fontSize": 11
49
+ };
@@ -0,0 +1,141 @@
1
+ // The look of a WezTerm window supercode opens: Apple's Terminal.app, so a person from macOS feels at home (the
2
+ // owner's ruling). On a Mac the window takes this machine's own Terminal.app default profile: its colours, face, size,
3
+ // spacing, cursor, Option key, bell, opacity, blur and window size, read from com.apple.Terminal and converted to sRGB
4
+ // by AppKit itself. Whatever the profile leaves unset, and every slot on a machine without Terminal.app, is Terminal.app's
5
+ // Basic profile (terminal-basic.mjs, the brand's terminal roles), light or dark with the system's appearance as Basic is.
6
+ // The config is WezTerm's own file, passed with `--config-file`; the person's ~/.wezterm.lua is not read for it.
7
+ import { execFileSync } from 'node:child_process';
8
+ import { existsSync, renameSync, writeFileSync } from 'node:fs';
9
+ import { tmpdir } from 'node:os';
10
+ import { join } from 'node:path';
11
+ import { TERMINAL_BASIC } from './terminal-basic.mjs';
12
+
13
+ const TERMINAL_FONTS = '/System/Applications/Utilities/Terminal.app/Contents/Resources/Fonts';
14
+ // Where CoreText finds a glyph Terminal.app's face lacks, in its order (CTFontCopyDefaultCascadeListForLanguages for
15
+ // SFMonoTerminal-Regular, then the faces it picked for ⎿ and ⏺ on macOS 26): WezTerm's own fallbacks come after.
16
+ // CoreText's cascade holds a subset of Apple Symbols (.AppleSymbolsFB) that lacks ⎿, which it draws from Hiragino
17
+ // Sans; WezTerm reads the whole face, so Apple Symbols comes after the faces CoreText reached first.
18
+ const MAC_CASCADE = ['Menlo', 'Monaco', 'Courier New', 'Helvetica', 'Lucida Grande', 'PingFang SC', 'Hiragino Sans', 'STIX Two Math', 'Apple Symbols', 'Apple Color Emoji'];
19
+ const ANSI = ['Black', 'Red', 'Green', 'Yellow', 'Blue', 'Magenta', 'Cyan', 'White'];
20
+
21
+ // Runs in osascript's JavaScript for Automation with the Objective-C bridge: no Apple Event is sent, so no automation
22
+ // permission is asked for. Terminal.app's own faces (SF Mono Terminal) are registered first, for the process only, so
23
+ // the profile's archived font resolves to its family.
24
+ const READ_PROFILE = `
25
+ ObjC.import('AppKit'); ObjC.import('CoreText');
26
+ function run() {
27
+ const defaults = $.NSUserDefaults.alloc.initWithSuiteName('com.apple.Terminal');
28
+ const name = ObjC.unwrap(defaults.stringForKey('Default Window Settings'));
29
+ const profiles = defaults.dictionaryForKey('Window Settings');
30
+ if (!name || profiles.isNil()) return 'null';
31
+ const profile = profiles.objectForKey(name);
32
+ if (profile.isNil()) return JSON.stringify({ name });
33
+ const dir = ${JSON.stringify(TERMINAL_FONTS)};
34
+ const files = $.NSFileManager.defaultManager.contentsOfDirectoryAtPathError(dir, null);
35
+ if (!files.isNil()) for (const file of ObjC.deepUnwrap(files)) $.CTFontManagerRegisterFontsForURL($.NSURL.fileURLWithPath(dir + '/' + file), 1, null);
36
+ const value = (key) => { const item = profile.objectForKey(key); return item.isNil() ? undefined : ObjC.deepUnwrap(item); };
37
+ const archived = (key) => { const data = profile.objectForKey(key); return data.isNil() ? null : $.NSKeyedUnarchiver.unarchiveObjectWithData(data); };
38
+ const colors = {};
39
+ for (const key of ObjC.deepUnwrap(profile.allKeys)) {
40
+ if (!/Color$/.test(key)) continue;
41
+ const color = archived(key);
42
+ const srgb = color && !color.isNil() ? color.colorUsingColorSpace($.NSColorSpace.sRGBColorSpace) : null;
43
+ if (srgb && !srgb.isNil()) colors[key] = [srgb.redComponent, srgb.greenComponent, srgb.blueComponent, srgb.alphaComponent];
44
+ }
45
+ const font = archived('Font');
46
+ return JSON.stringify({
47
+ name, colors,
48
+ font: font && !font.isNil() ? { family: ObjC.unwrap(font.familyName), size: font.pointSize, lineHeight: $.NSLayoutManager.alloc.init.defaultLineHeightForFont(font), ascender: font.ascender, descender: font.descender } : null,
49
+ widthSpacing: value('FontWidthSpacing'), heightSpacing: value('FontHeightSpacing'),
50
+ cursorType: value('CursorType'), cursorBlink: value('CursorBlink'), optionIsMeta: value('useOptionAsMetaKey'),
51
+ bell: value('Bell'), visualBell: value('VisualBell'), brightBold: value('UseBrightBold'),
52
+ blur: value('BackgroundBlur'), columns: value('columnCount'), rows: value('rowCount'),
53
+ });
54
+ }`;
55
+
56
+ /** This machine's Terminal.app default profile, as read by AppKit; null off macOS or when it cannot be read. */
57
+ export function terminalAppProfile(platform = process.platform) {
58
+ if (platform !== 'darwin') return null;
59
+ try {
60
+ return JSON.parse(execFileSync('osascript', ['-l', 'JavaScript', '-e', READ_PROFILE], { encoding: 'utf8', timeout: 5_000, stdio: ['ignore', 'pipe', 'ignore'] }));
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
65
+
66
+ const hex = ([r, g, b]) => `#${[r, g, b].map((part) => Math.round(Math.min(1, Math.max(0, part)) * 255).toString(16).padStart(2, '0')).join('')}`;
67
+ const lua = (value) => (typeof value === 'string' ? JSON.stringify(value) : String(value));
68
+
69
+ /** WezTerm's config (Lua) for a window that looks like Terminal.app with `profile` (from terminalAppProfile, or null). */
70
+ export function weztermConfig(profile, { fontDirs = existsSync(TERMINAL_FONTS) ? [TERMINAL_FONTS] : [] } = {}) {
71
+ const mac = fontDirs.length > 0;
72
+ const stored = profile?.colors ?? {};
73
+ // One colour table per appearance: the profile's colour where it stores one, else Basic's for that appearance.
74
+ const scheme = (basic) => {
75
+ const pick = (key, slot) => (stored[key] ? hex(stored[key]) : basic[slot]);
76
+ const background = pick('BackgroundColor', 'background');
77
+ const foreground = pick('TextColor', 'foreground');
78
+ const cursor = pick('CursorColor', 'cursor');
79
+ return {
80
+ background, foreground,
81
+ cursor_bg: cursor, cursor_border: cursor, cursor_fg: background,
82
+ selection_bg: pick('SelectionColor', 'selection'), selection_fg: foreground,
83
+ ansi: ANSI.map((name) => pick(`ANSI${name}Color`, name.toLowerCase())),
84
+ brights: ANSI.map((name) => pick(`ANSIBright${name}Color`, `bright${name}`)),
85
+ };
86
+ };
87
+ const table = (colors) => `{\n${Object.entries(colors).map(([key, value]) => ` ${key} = ${Array.isArray(value) ? `{ ${value.map(lua).join(', ')} }` : lua(value)},`).join('\n')}\n }`;
88
+ const families = mac ? [...new Set([profile?.font?.family ?? 'SF Mono Terminal', ...MAC_CASCADE])] : [];
89
+ // A row is AppKit's line height for the face (Terminal.app's), where WezTerm's is the face's ascent plus descent.
90
+ const font = profile?.font;
91
+ const rowScale = font?.lineHeight && font.ascender - font.descender > 0 ? font.lineHeight / (font.ascender - font.descender) : 1;
92
+ const opacity = stored.BackgroundColor?.[3] ?? 1;
93
+ const cursorShape = ['Block', 'Underline', 'Bar'][profile?.cursorType ?? 0] ?? 'Block';
94
+ const composed = !profile?.optionIsMeta;
95
+ const lines = [
96
+ '-- Generated by supercode (sdk/teams/terminal-look.mjs): this window looks like Terminal.app' + (profile?.name ? ` with this machine's "${profile.name}" profile.` : "'s Basic profile."),
97
+ "local wezterm = require 'wezterm'",
98
+ 'local config = wezterm.config_builder()',
99
+ `local schemes = {\n light = ${table(scheme(TERMINAL_BASIC.light))},\n dark = ${table(scheme(TERMINAL_BASIC.dark))},\n}`,
100
+ "local appearance = wezterm.gui and wezterm.gui.get_appearance() or 'Dark'",
101
+ "config.colors = appearance:find('Dark') and schemes.dark or schemes.light",
102
+ `config.font_dirs = { ${fontDirs.map(lua).join(', ')} }`,
103
+ ...(families.length ? [`config.font = wezterm.font_with_fallback({ ${families.map(lua).join(', ')} })`] : []),
104
+ // CoreText draws glyphs unhinted; FreeType's default hinting snaps their outlines to the pixel grid.
105
+ "config.freetype_load_flags = 'NO_HINTING'",
106
+ // Box and block characters are the face's own glyphs, as CoreText draws them, not WezTerm's drawn lines.
107
+ 'config.custom_block_glyphs = false',
108
+ `config.font_size = ${profile?.font?.size ?? TERMINAL_BASIC.fontSize}`,
109
+ `config.cell_width = ${profile?.widthSpacing ?? 1}`,
110
+ `config.line_height = ${Number(((profile?.heightSpacing ?? 1) * rowScale).toFixed(4))}`,
111
+ `config.default_cursor_style = ${lua(`${profile?.cursorBlink ? 'Blinking' : 'Steady'}${cursorShape}`)}`,
112
+ `config.send_composed_key_when_left_alt_is_pressed = ${composed}`,
113
+ `config.send_composed_key_when_right_alt_is_pressed = ${composed}`,
114
+ `config.audible_bell = ${lua(profile?.bell === false ? 'Disabled' : 'SystemBeep')}`,
115
+ ...(profile?.visualBell ? ["config.visual_bell = { fade_in_duration_ms = 75, fade_out_duration_ms = 75, target = 'BackgroundColor' }"] : []),
116
+ ...(typeof profile?.brightBold === 'boolean' ? [`config.bold_brightens_ansi_colors = ${profile.brightBold}`] : []),
117
+ `config.window_background_opacity = ${opacity}`,
118
+ // Terminal.app's blur is 0 to 1; WezTerm's is a radius. 40 at full blur is an estimate, not a measurement.
119
+ `config.macos_window_background_blur = ${opacity < 1 ? Math.round((profile?.blur ?? 0) * 40) : 0}`,
120
+ `config.initial_cols = ${profile?.columns ?? 80}`,
121
+ `config.initial_rows = ${profile?.rows ?? 24}`,
122
+ // Terminal.app's margin around the text is not measured; this padding is an estimate.
123
+ 'config.window_padding = { left = 4, right = 4, top = 2, bottom = 2 }',
124
+ 'config.hide_tab_bar_if_only_one_tab = true',
125
+ 'config.enable_scroll_bar = false',
126
+ // Terminal.app does not ask before closing a window whose process is tmux (its default "ask before closing" list).
127
+ "config.window_close_confirmation = 'NeverPrompt'",
128
+ 'return config',
129
+ '',
130
+ ];
131
+ return lines.join('\n');
132
+ }
133
+
134
+ /** Writes the config for this machine and returns its path. */
135
+ export function weztermConfigFile(platform = process.platform) {
136
+ const path = join(tmpdir(), `supercode-wezterm-${process.getuid?.() ?? 'user'}.lua`);
137
+ const next = `${path}.${process.pid}`;
138
+ writeFileSync(next, weztermConfig(terminalAppProfile(platform)));
139
+ renameSync(next, path);
140
+ return path;
141
+ }