instar 1.3.1611 → 1.3.1613
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/dashboard/console/console.css +3 -1
- package/dashboard/console/console.js +18 -5
- package/dashboard/console/home-model.js +4 -1
- package/dashboard/console/messages-model.js +2 -1
- package/dashboard/console/messages.js +1 -1
- package/dashboard/console/pace-model.js +5 -0
- package/dashboard/console/pace.js +3 -1
- package/dashboard/console/plans-model.js +17 -1
- package/dashboard/console/promises.js +2 -1
- package/dashboard/console/secrets-model.js +11 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +32 -8
- package/dist/commands/server.js.map +1 -1
- package/dist/core/AppConversationStore.d.ts +2 -0
- package/dist/core/AppConversationStore.d.ts.map +1 -1
- package/dist/core/AppConversationStore.js +8 -1
- package/dist/core/AppConversationStore.js.map +1 -1
- package/dist/core/AppRecordSignatures.d.ts +23 -2
- package/dist/core/AppRecordSignatures.d.ts.map +1 -1
- package/dist/core/AppRecordSignatures.js +32 -5
- package/dist/core/AppRecordSignatures.js.map +1 -1
- package/dist/core/LocalAttentionItems.d.ts +49 -0
- package/dist/core/LocalAttentionItems.d.ts.map +1 -0
- package/dist/core/LocalAttentionItems.js +107 -0
- package/dist/core/LocalAttentionItems.js.map +1 -0
- package/dist/core/MacDesktopHost.d.ts +3 -0
- package/dist/core/MacDesktopHost.d.ts.map +1 -1
- package/dist/core/MacDesktopHost.js +10 -0
- package/dist/core/MacDesktopHost.js.map +1 -1
- package/dist/core/MachineLockStateReader.d.ts.map +1 -1
- package/dist/core/MachineLockStateReader.js +6 -1
- package/dist/core/MachineLockStateReader.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +6 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +59 -2
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/types.d.ts +12 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/data/standards-guard-index.json +1 -1
- package/dist/data/standards-guard-index.meta.json +2 -2
- package/dist/data/standards-registry.meta.json +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts +2 -0
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +28 -0
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/localVerify/localVerifyWiring.d.ts +11 -0
- package/dist/localVerify/localVerifyWiring.d.ts.map +1 -1
- package/dist/localVerify/localVerifyWiring.js +9 -0
- package/dist/localVerify/localVerifyWiring.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts +8 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +46 -31
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/messaging/shared/MessageLogger.d.ts +2 -0
- package/dist/messaging/shared/MessageLogger.d.ts.map +1 -1
- package/dist/messaging/shared/MessageLogger.js.map +1 -1
- package/dist/messaging/slack/SlackAdapter.d.ts +19 -0
- package/dist/messaging/slack/SlackAdapter.d.ts.map +1 -1
- package/dist/messaging/slack/SlackAdapter.js +21 -0
- package/dist/messaging/slack/SlackAdapter.js.map +1 -1
- package/dist/messaging/telegram-origin/OriginForwardToHolder.d.ts +13 -0
- package/dist/messaging/telegram-origin/OriginForwardToHolder.d.ts.map +1 -1
- package/dist/messaging/telegram-origin/OriginForwardToHolder.js +16 -0
- package/dist/messaging/telegram-origin/OriginForwardToHolder.js.map +1 -1
- package/dist/messaging/telegram-origin/OriginLegacy.d.ts +8 -0
- package/dist/messaging/telegram-origin/OriginLegacy.d.ts.map +1 -1
- package/dist/messaging/telegram-origin/OriginLegacy.js +34 -6
- package/dist/messaging/telegram-origin/OriginLegacy.js.map +1 -1
- package/dist/messaging/telegram-origin/TelegramOriginBoot.d.ts +3 -0
- package/dist/messaging/telegram-origin/TelegramOriginBoot.d.ts.map +1 -1
- package/dist/messaging/telegram-origin/TelegramOriginBoot.js +54 -10
- package/dist/messaging/telegram-origin/TelegramOriginBoot.js.map +1 -1
- package/dist/messaging/telegram-origin/types.d.ts +11 -0
- package/dist/messaging/telegram-origin/types.d.ts.map +1 -1
- package/dist/messaging/telegram-origin/types.js +15 -0
- package/dist/messaging/telegram-origin/types.js.map +1 -1
- package/dist/monitoring/GreenPrAutoMerger.d.ts.map +1 -1
- package/dist/monitoring/GreenPrAutoMerger.js +13 -0
- package/dist/monitoring/GreenPrAutoMerger.js.map +1 -1
- package/dist/monitoring/greenPrAutomergeWiring.d.ts.map +1 -1
- package/dist/monitoring/greenPrAutomergeWiring.js +3 -0
- package/dist/monitoring/greenPrAutomergeWiring.js.map +1 -1
- package/dist/monitoring/promptRelaySettle.d.ts +35 -0
- package/dist/monitoring/promptRelaySettle.d.ts.map +1 -0
- package/dist/monitoring/promptRelaySettle.js +58 -0
- package/dist/monitoring/promptRelaySettle.js.map +1 -0
- package/dist/native/instar-desktop-host +0 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +3 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +15 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +78 -20
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.js +2 -2
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/mobileConversationRoutes.d.ts +16 -3
- package/dist/server/mobileConversationRoutes.d.ts.map +1 -1
- package/dist/server/mobileConversationRoutes.js +36 -8
- package/dist/server/mobileConversationRoutes.js.map +1 -1
- package/dist/server/mobilePairingRoutes.d.ts +1 -1
- package/dist/server/mobilePairingRoutes.d.ts.map +1 -1
- package/dist/server/mobilePairingRoutes.js +5 -2
- package/dist/server/mobilePairingRoutes.js.map +1 -1
- package/dist/server/mobileSlackRoutes.d.ts +162 -0
- package/dist/server/mobileSlackRoutes.d.ts.map +1 -0
- package/dist/server/mobileSlackRoutes.js +552 -0
- package/dist/server/mobileSlackRoutes.js.map +1 -0
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +85 -6
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/build-locked-proof.mjs +125 -0
- package/scripts/lib/locked-proof-build.mjs +71 -0
- package/scripts/locked-proof/configure-proof-agent.mjs +68 -0
- package/scripts/locked-proof/create-proof-user.sh +89 -0
- package/scripts/locked-proof/driver.mjs +457 -0
- package/scripts/locked-proof/lib.mjs +267 -0
- package/scripts/locked-proof/pty-feed.py +59 -0
- package/scripts/locked-proof/remove-proof-user.sh +18 -0
- package/scripts/locked-proof/reset-attempts.py +52 -0
- package/scripts/locked-proof/stage-proof-agents.sh +68 -0
- package/src/data/builtin-manifest.json +65 -65
- package/src/data/standards-guard-index.json +1 -1
- package/src/data/standards-guard-index.meta.json +2 -2
- package/src/data/standards-registry.meta.json +1 -1
- package/src/scaffold/templates.ts +3 -1
- package/upgrades/1.3.1612.md +72 -0
- package/upgrades/1.3.1613.md +108 -0
- package/upgrades/side-effects/blocker-text-is-the-message.eli16.md +5 -0
- package/upgrades/side-effects/blocker-text-is-the-message.md +38 -0
- package/upgrades/side-effects/console-needs-row-tidy.eli16.md +13 -0
- package/upgrades/side-effects/console-needs-row-tidy.md +24 -0
- package/upgrades/side-effects/console-partial-survives-reload.eli16.md +5 -0
- package/upgrades/side-effects/console-partial-survives-reload.md +34 -0
- package/upgrades/side-effects/console-secret-drop-names.eli16.md +5 -0
- package/upgrades/side-effects/console-secret-drop-names.md +34 -0
- package/upgrades/side-effects/desktop-tests-stop-touching-tcc.eli16.md +13 -0
- package/upgrades/side-effects/desktop-tests-stop-touching-tcc.md +65 -0
- package/upgrades/side-effects/green-pr-eligible-needs-passing-record.eli16.md +7 -0
- package/upgrades/side-effects/green-pr-eligible-needs-passing-record.md +39 -0
- package/upgrades/side-effects/green-pr-poke-unknown-mergeable.eli16.md +5 -0
- package/upgrades/side-effects/green-pr-poke-unknown-mergeable.md +41 -0
- package/upgrades/side-effects/held-expiry-one-row-per-topic.eli16.md +5 -0
- package/upgrades/side-effects/held-expiry-one-row-per-topic.md +37 -0
- package/upgrades/side-effects/locked-proof-driver.md +72 -0
- package/upgrades/side-effects/locked-proof-test-build.md +64 -0
- package/upgrades/side-effects/locked-security-item-no-telegram.md +78 -0
- package/upgrades/side-effects/mobile-native-conversations-inc4.md +115 -0
- package/upgrades/side-effects/origin-config-unknown-not-missing.eli16.md +16 -0
- package/upgrades/side-effects/origin-config-unknown-not-missing.md +38 -0
- package/upgrades/side-effects/pool-rows-use-local-machine-name.eli16.md +5 -0
- package/upgrades/side-effects/pool-rows-use-local-machine-name.md +34 -0
- package/upgrades/side-effects/prompt-gate-settle-before-relay.eli16.md +11 -0
- package/upgrades/side-effects/prompt-gate-settle-before-relay.md +57 -0
- package/upgrades/side-effects/topic-names-through-lifeline.eli16.md +9 -0
- package/upgrades/side-effects/topic-names-through-lifeline.md +39 -0
package/package.json
CHANGED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// safe-git-allow: read-only git (rev-parse, show, archive) and removal of the build's own node_modules link inside its scratch tree; nothing in a checkout is changed.
|
|
3
|
+
/**
|
|
4
|
+
* build-locked-proof.mjs — the locked-screen TEST BUILD (docs/specs/desktop-locked-screen.md,
|
|
5
|
+
* phase 3: "That build alone compiles in a test proven-versions table").
|
|
6
|
+
*
|
|
7
|
+
* It copies one committed revision into a scratch directory OUTSIDE this checkout,
|
|
8
|
+
* adds a single `PROOF-RUN-IN-PROGRESS` row for one macOS major version to the
|
|
9
|
+
* scratch copy of src/core/DesktopProvenVersions.ts, sets the package version to
|
|
10
|
+
* `<release>-lockedproof.<n>`, builds it (TypeScript + the unchanged Swift helper)
|
|
11
|
+
* and packs it. The committed table is never touched, nothing is published, and
|
|
12
|
+
* the helper source is the shipped one, so its source hash binds the proof
|
|
13
|
+
* evidence to the helper the fleet will run.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* node scripts/build-locked-proof.mjs [--os-major N] [--n N] [--ref REF] [--out DIR] [--dry-run]
|
|
17
|
+
*
|
|
18
|
+
* --os-major macOS major to admit (default: this Mac's `sw_vers -productVersion`)
|
|
19
|
+
* --n build number for the version suffix (default 1)
|
|
20
|
+
* --ref git revision to build (default HEAD; committed content only)
|
|
21
|
+
* --out where the scratch tree, .tgz and manifest go (default $TMPDIR/instar-locked-proof)
|
|
22
|
+
* --dry-run stop after the rewrite: print the row and the diff, build nothing
|
|
23
|
+
*
|
|
24
|
+
* Output: `<out>/instar-<version>.tgz` and `<out>/locked-proof-manifest-<version>.json`
|
|
25
|
+
* ({ packageVersion, commit, osMajor, helperSourceHash, tgz, tgzSha256, createdAt }).
|
|
26
|
+
* Install only into a throwaway macOS user's own prefix, with auto-update off.
|
|
27
|
+
*/
|
|
28
|
+
import { execFileSync } from 'node:child_process';
|
|
29
|
+
import crypto from 'node:crypto';
|
|
30
|
+
import fs from 'node:fs';
|
|
31
|
+
import os from 'node:os';
|
|
32
|
+
import path from 'node:path';
|
|
33
|
+
import { fileURLToPath } from 'node:url';
|
|
34
|
+
import { parseOsMajor, proofVersion, rewriteProvenTable } from './lib/locked-proof-build.mjs';
|
|
35
|
+
|
|
36
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
37
|
+
const TABLE_FILE = 'src/core/DesktopProvenVersions.ts';
|
|
38
|
+
|
|
39
|
+
function arg(name) {
|
|
40
|
+
const i = process.argv.indexOf(`--${name}`);
|
|
41
|
+
return i >= 0 ? process.argv[i + 1] : undefined;
|
|
42
|
+
}
|
|
43
|
+
const dryRun = process.argv.includes('--dry-run');
|
|
44
|
+
|
|
45
|
+
function git(args, opts = {}) {
|
|
46
|
+
return execFileSync('git', args, { cwd: ROOT, encoding: 'utf8', ...opts });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function localOsMajor() {
|
|
50
|
+
if (process.platform !== 'darwin') throw new Error('--os-major is required when not running on a Mac');
|
|
51
|
+
return parseOsMajor(execFileSync('sw_vers', ['-productVersion'], { encoding: 'utf8' }));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function main() {
|
|
55
|
+
const osMajor = arg('os-major') !== undefined ? parseOsMajor(arg('os-major')) : localOsMajor();
|
|
56
|
+
const n = arg('n') !== undefined ? Number.parseInt(arg('n'), 10) : 1;
|
|
57
|
+
const ref = arg('ref') ?? 'HEAD';
|
|
58
|
+
const out = path.resolve(arg('out') ?? path.join(os.tmpdir(), 'instar-locked-proof'));
|
|
59
|
+
const commit = git(['rev-parse', '--verify', `${ref}^{commit}`]).trim();
|
|
60
|
+
if (out === ROOT || out.startsWith(ROOT + path.sep)) throw new Error(`--out must be outside this checkout (${ROOT})`);
|
|
61
|
+
|
|
62
|
+
const committedTable = git(['show', `${commit}:${TABLE_FILE}`]);
|
|
63
|
+
const basePkg = JSON.parse(git(['show', `${commit}:package.json`]));
|
|
64
|
+
const version = proofVersion(basePkg.version, n);
|
|
65
|
+
const rewritten = rewriteProvenTable(committedTable, { osMajor, version });
|
|
66
|
+
|
|
67
|
+
const tableHashBefore = fs.existsSync(path.join(ROOT, TABLE_FILE))
|
|
68
|
+
? crypto.createHash('sha256').update(fs.readFileSync(path.join(ROOT, TABLE_FILE))).digest('hex') : null;
|
|
69
|
+
|
|
70
|
+
if (dryRun) {
|
|
71
|
+
console.log(`[locked-proof] dry run: ${version} at ${commit.slice(0, 12)} admits macOS ${osMajor}`);
|
|
72
|
+
console.log(rewritten.split('\n').filter(line => line.includes('PROVEN_MACOS_VERSIONS')).join('\n'));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const scratch = path.join(out, `instar-${version}`);
|
|
77
|
+
if (fs.existsSync(scratch)) throw new Error(`${scratch} exists; pick another --n or remove it`);
|
|
78
|
+
fs.mkdirSync(scratch, { recursive: true });
|
|
79
|
+
|
|
80
|
+
// Committed content only: an uncommitted edit in this checkout never reaches the build.
|
|
81
|
+
const archive = execFileSync('git', ['archive', '--format=tar', commit], { cwd: ROOT, maxBuffer: 1 << 30 });
|
|
82
|
+
execFileSync('tar', ['-x', '-C', scratch], { input: archive, maxBuffer: 1 << 30 });
|
|
83
|
+
|
|
84
|
+
fs.writeFileSync(path.join(scratch, TABLE_FILE), rewritten);
|
|
85
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(scratch, 'package.json'), 'utf8'));
|
|
86
|
+
pkg.version = version;
|
|
87
|
+
fs.writeFileSync(path.join(scratch, 'package.json'), `${JSON.stringify(pkg, null, 2)}\n`);
|
|
88
|
+
fs.symlinkSync(path.join(ROOT, 'node_modules'), path.join(scratch, 'node_modules'));
|
|
89
|
+
|
|
90
|
+
console.log(`[locked-proof] building ${version} (macOS ${osMajor}) in ${scratch}`);
|
|
91
|
+
// Never release-signed: the lock-file signer reads these env keys (and a local .instar-release-keys/, which a
|
|
92
|
+
// fresh git archive never contains), and a test build must not carry a release signature.
|
|
93
|
+
const buildEnv = { ...process.env };
|
|
94
|
+
delete buildEnv.INSTAR_RELEASE_PRIVATE_KEY_PEM;
|
|
95
|
+
delete buildEnv.INSTAR_RELEASE_PRIVATE_KEY_PEM_PATH;
|
|
96
|
+
delete buildEnv.INSTAR_RELEASE_BUILD;
|
|
97
|
+
execFileSync('npm', ['run', 'build'], { cwd: scratch, stdio: 'inherit', env: buildEnv });
|
|
98
|
+
// The symlink must not be packed; npm pack ignores node_modules, but drop it to be sure.
|
|
99
|
+
fs.unlinkSync(path.join(scratch, 'node_modules'));
|
|
100
|
+
const packed = execFileSync('npm', ['pack', '--ignore-scripts', '--pack-destination', out], { cwd: scratch, encoding: 'utf8' })
|
|
101
|
+
.trim().split('\n').pop();
|
|
102
|
+
const tgz = path.join(out, packed);
|
|
103
|
+
|
|
104
|
+
const helper = JSON.parse(fs.readFileSync(path.join(scratch, 'dist/native/instar-desktop-host.json'), 'utf8'));
|
|
105
|
+
const manifest = {
|
|
106
|
+
packageVersion: version, commit, osMajor, helperSourceHash: helper.sourceHash,
|
|
107
|
+
tgz, tgzSha256: crypto.createHash('sha256').update(fs.readFileSync(tgz)).digest('hex'),
|
|
108
|
+
createdAt: new Date().toISOString(),
|
|
109
|
+
};
|
|
110
|
+
const manifestPath = path.join(out, `locked-proof-manifest-${version}.json`);
|
|
111
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
112
|
+
|
|
113
|
+
const tableHashAfter = fs.existsSync(path.join(ROOT, TABLE_FILE))
|
|
114
|
+
? crypto.createHash('sha256').update(fs.readFileSync(path.join(ROOT, TABLE_FILE))).digest('hex') : null;
|
|
115
|
+
if (tableHashBefore !== tableHashAfter) throw new Error(`${TABLE_FILE} in this checkout changed during the build`);
|
|
116
|
+
|
|
117
|
+
console.log(`[locked-proof] ${tgz}`);
|
|
118
|
+
console.log(`[locked-proof] manifest ${manifestPath}`);
|
|
119
|
+
console.log(`[locked-proof] helper sourceHash ${helper.sourceHash}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try { main(); } catch (err) {
|
|
123
|
+
console.error(`[locked-proof] ${err instanceof Error ? err.message : String(err)}`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for scripts/build-locked-proof.mjs (docs/specs/desktop-locked-screen.md,
|
|
3
|
+
* phase 3 "test build"). Kept separate so the rewrite is unit-tested without a build.
|
|
4
|
+
*
|
|
5
|
+
* The test build is the ONLY way a macOS version without recorded P12/P15 passes
|
|
6
|
+
* reaches the proven-versions table, and it only ever does so in a scratch copy.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** The marker every test-build row carries instead of a proof path. */
|
|
10
|
+
export const PROOF_IN_PROGRESS = 'PROOF-RUN-IN-PROGRESS';
|
|
11
|
+
|
|
12
|
+
/** The literal the committed table is declared with. Exactly one must exist. */
|
|
13
|
+
const TABLE_RE = /export const PROVEN_MACOS_VERSIONS: readonly DesktopProvenVersion\[\] = Object\.freeze\(\[([\s\S]*?)\]\);/g;
|
|
14
|
+
|
|
15
|
+
/** `<base>-lockedproof.<n>`; base must be a plain dotted release version. */
|
|
16
|
+
export function proofVersion(base, n) {
|
|
17
|
+
if (typeof base !== 'string' || !/^\d+\.\d+\.\d+$/.test(base)) {
|
|
18
|
+
throw new Error(`package version "${base}" is not a plain release version (x.y.z)`);
|
|
19
|
+
}
|
|
20
|
+
if (!Number.isSafeInteger(n) || n < 1) throw new Error(`build number must be a positive integer, got ${n}`);
|
|
21
|
+
return `${base}-lockedproof.${n}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function parseOsMajor(value) {
|
|
25
|
+
const major = Number.parseInt(String(value).trim().split('.')[0] ?? '', 10);
|
|
26
|
+
if (!Number.isSafeInteger(major) || major < 11 || major > 99) {
|
|
27
|
+
throw new Error(`not a macOS major version: "${value}"`);
|
|
28
|
+
}
|
|
29
|
+
return major;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Return the source of DesktopProvenVersions.ts with one test row for `osMajor`
|
|
34
|
+
* added in front of the committed rows. Refuses when the declaration is not
|
|
35
|
+
* found exactly once, or when that version is already in the table.
|
|
36
|
+
*/
|
|
37
|
+
export function rewriteProvenTable(source, { osMajor, version }) {
|
|
38
|
+
const matches = [...source.matchAll(TABLE_RE)];
|
|
39
|
+
if (matches.length !== 1) {
|
|
40
|
+
throw new Error(`expected exactly one PROVEN_MACOS_VERSIONS declaration, found ${matches.length}`);
|
|
41
|
+
}
|
|
42
|
+
const [whole, body] = matches[0];
|
|
43
|
+
if (new RegExp(`osMajor:\\s*${osMajor}\\b`).test(body)) {
|
|
44
|
+
throw new Error(`macOS ${osMajor} is already in the committed table; no test build is needed`);
|
|
45
|
+
}
|
|
46
|
+
const marker = `${PROOF_IN_PROGRESS} ${version}`;
|
|
47
|
+
const row = `{ osMajor: ${osMajor}, p12: '${marker}', p15: '${marker}', refuse: [] }`;
|
|
48
|
+
const rest = body.trim();
|
|
49
|
+
const next = whole.replace(`([${body}])`, `([${row}${rest ? `, ${rest}` : ''}])`);
|
|
50
|
+
return source.replace(whole, next);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check a table's rows against the proof records on disk. A committed row must
|
|
55
|
+
* cite, for p12 and p15, a file under docs/proofs/desktop-locked-screen/ that
|
|
56
|
+
* exists (an `#anchor` is allowed), and never the test-build marker.
|
|
57
|
+
* Returns a list of problems (empty when every row is backed).
|
|
58
|
+
*/
|
|
59
|
+
export function unprovenRows(rows, fileExists) {
|
|
60
|
+
const problems = [];
|
|
61
|
+
for (const row of rows) {
|
|
62
|
+
for (const gate of ['p12', 'p15']) {
|
|
63
|
+
const cite = String(row[gate] ?? '');
|
|
64
|
+
const file = cite.split('#')[0];
|
|
65
|
+
if (cite.includes(PROOF_IN_PROGRESS)) problems.push(`macOS ${row.osMajor} ${gate}: test-build marker in the committed table`);
|
|
66
|
+
else if (!file.startsWith('docs/proofs/desktop-locked-screen/') || file.includes('..')) problems.push(`macOS ${row.osMajor} ${gate}: "${cite}" is not a proof record path`);
|
|
67
|
+
else if (!fileExists(file)) problems.push(`macOS ${row.osMajor} ${gate}: proof record ${file} does not exist`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return problems;
|
|
71
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Set a proof agent's config for the locked-Mac proof run. Runs as the proof user.
|
|
4
|
+
*
|
|
5
|
+
* configure-proof-agent.mjs <agent-dir> --port N [--tunnel] [--front] --execute
|
|
6
|
+
*
|
|
7
|
+
* Without --execute it prints what it would change. It refuses any directory that
|
|
8
|
+
* does not resolve inside /Users/instarproof (the throwaway proof user's home).
|
|
9
|
+
*
|
|
10
|
+
* - remote desktop on, the kept helper on (the proof user's own copy), locked-screen
|
|
11
|
+
* access left absent (off): the run turns it on with the dashboard PIN route.
|
|
12
|
+
* - updates never auto-applied: the updater would replace the -lockedproof build.
|
|
13
|
+
* - no start-on-login job: the driver starts and stops the server itself.
|
|
14
|
+
* - --tunnel: a Cloudflare quick tunnel (the password box needs a first-tier tunnel).
|
|
15
|
+
* - --front: this agent is its own designated desktop front (needs the machine id,
|
|
16
|
+
* which exists after the first server start; run again with --front then).
|
|
17
|
+
* Only missing or listed keys change; everything else in config.json is kept.
|
|
18
|
+
*/
|
|
19
|
+
import fs from 'node:fs';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
|
|
22
|
+
export const PROOF_HOME = '/Users/instarproof';
|
|
23
|
+
|
|
24
|
+
/** True only for a resolved path inside the proof user's home. */
|
|
25
|
+
export function isInsideProofHome(resolved) {
|
|
26
|
+
return resolved === PROOF_HOME || resolved.startsWith(`${PROOF_HOME}/`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function proofConfig(config, { port, tunnel, frontMachineId }) {
|
|
30
|
+
const next = { ...config, port };
|
|
31
|
+
next.remoteDesktop = { ...(config.remoteDesktop ?? {}), enabled: true, retainedHelper: true };
|
|
32
|
+
if (frontMachineId) next.remoteDesktop.frontMachineId = frontMachineId;
|
|
33
|
+
delete next.remoteDesktop.lockedScreen;
|
|
34
|
+
next.updates = { ...(config.updates ?? {}), autoApply: false };
|
|
35
|
+
next.autostart = { ...(config.autostart ?? {}), autoInstall: false };
|
|
36
|
+
next.messaging = [];
|
|
37
|
+
if (tunnel) next.tunnel = { ...(config.tunnel ?? {}), enabled: true, type: 'quick' };
|
|
38
|
+
else if (next.tunnel) next.tunnel = { ...next.tunnel, enabled: false };
|
|
39
|
+
return next;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function main() {
|
|
43
|
+
const [dir, ...rest] = process.argv.slice(2);
|
|
44
|
+
const opt = name => { const i = rest.indexOf(`--${name}`); return i >= 0 ? rest[i + 1] : undefined; };
|
|
45
|
+
const port = Number(opt('port'));
|
|
46
|
+
if (!dir || !Number.isSafeInteger(port)) { process.stderr.write('usage: configure-proof-agent.mjs <agent-dir> --port N [--tunnel] [--front]\n'); process.exit(2); }
|
|
47
|
+
const real = fs.realpathSync(dir);
|
|
48
|
+
if (!isInsideProofHome(real)) { process.stderr.write(`refusing: ${real} is not inside ${PROOF_HOME}\n`); process.exit(1); }
|
|
49
|
+
const file = path.join(real, '.instar', 'config.json');
|
|
50
|
+
const config = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
51
|
+
let frontMachineId;
|
|
52
|
+
if (rest.includes('--front')) {
|
|
53
|
+
const identity = path.join(real, '.instar', 'machine', 'identity.json');
|
|
54
|
+
if (!fs.existsSync(identity)) { process.stderr.write('no machine identity yet: start the server once, then run with --front\n'); process.exit(1); }
|
|
55
|
+
frontMachineId = JSON.parse(fs.readFileSync(identity, 'utf8')).machineId;
|
|
56
|
+
}
|
|
57
|
+
const next = proofConfig(config, { port, tunnel: rest.includes('--tunnel'), frontMachineId });
|
|
58
|
+
if (!rest.includes('--execute')) {
|
|
59
|
+
process.stdout.write(`dry run (no --execute): would write ${file}\n${JSON.stringify({ port: next.port, remoteDesktop: next.remoteDesktop, updates: next.updates, autostart: next.autostart, tunnel: next.tunnel ?? null })}\n`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
63
|
+
fs.writeFileSync(tmp, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
|
|
64
|
+
fs.renameSync(tmp, file);
|
|
65
|
+
process.stdout.write(`configured ${dir} (port ${port}${rest.includes('--tunnel') ? ', quick tunnel' : ''}${frontMachineId ? `, front ${frontMachineId}` : ''})\n`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === new URL(import.meta.url).pathname) main();
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Create the throwaway macOS user for the locked-Mac proof run (instarproof).
|
|
3
|
+
#
|
|
4
|
+
# create-proof-user.sh --agent-home DIR [--execute] [--replace-vault]
|
|
5
|
+
#
|
|
6
|
+
# Without --execute it only prints the plan. With --execute (run as an admin with
|
|
7
|
+
# passwordless sudo, on the proof Mac):
|
|
8
|
+
# 1. generates a throwaway password (20 lowercase letters/digits, layout-safe),
|
|
9
|
+
# 2. stores it in the agent's vault FIRST (credential-store.mjs, value on stdin),
|
|
10
|
+
# under key locked_proof_user_password, so no password exists that the vault lacks.
|
|
11
|
+
# credential-store.mjs writes Bitwarden first; this script refuses to run unless that
|
|
12
|
+
# helper hands Bitwarden the encoded item on stdin (an older copy put it on `bw`'s
|
|
13
|
+
# command line, where `ps` can read it),
|
|
14
|
+
# 3. creates the standard (non-admin) user with sysadminctl, answering its password
|
|
15
|
+
# prompt through a pseudo-terminal (never on a command line, never in a log),
|
|
16
|
+
# 4. clears the user's account policies, so macOS does not force a password change
|
|
17
|
+
# at first login and does not lock the account after failed attempts,
|
|
18
|
+
# 5. checks the stored password authenticates (dscl -authonly, again by prompt).
|
|
19
|
+
# The password is never printed. Remove everything with remove-proof-user.sh.
|
|
20
|
+
#
|
|
21
|
+
# Tests point LOCKED_PROOF_FAKE_BIN at stand-in commands; then sudo is not used.
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
25
|
+
PROOF_USER="instarproof"
|
|
26
|
+
VAULT_KEY="locked_proof_user_password"
|
|
27
|
+
AGENT_HOME=""
|
|
28
|
+
EXECUTE=0
|
|
29
|
+
REPLACE=0
|
|
30
|
+
while [ $# -gt 0 ]; do
|
|
31
|
+
case "$1" in
|
|
32
|
+
--agent-home) AGENT_HOME="$2"; shift 2 ;;
|
|
33
|
+
--execute) EXECUTE=1; shift ;;
|
|
34
|
+
--replace-vault) REPLACE=1; shift ;;
|
|
35
|
+
*) echo "unknown option: $1" >&2; exit 2 ;;
|
|
36
|
+
esac
|
|
37
|
+
done
|
|
38
|
+
[ -n "$AGENT_HOME" ] || { echo "--agent-home is required" >&2; exit 2; }
|
|
39
|
+
|
|
40
|
+
if [ -n "${LOCKED_PROOF_FAKE_BIN:-}" ]; then
|
|
41
|
+
SUDO=""; BIN="$LOCKED_PROOF_FAKE_BIN"
|
|
42
|
+
SYSADMINCTL="$BIN/sysadminctl"; DSCL="$BIN/dscl"; PWPOLICY="$BIN/pwpolicy"; STORE="$BIN/credential-store"
|
|
43
|
+
else
|
|
44
|
+
SUDO="sudo -n"
|
|
45
|
+
SYSADMINCTL="/usr/sbin/sysadminctl"; DSCL="/usr/bin/dscl"; PWPOLICY="/usr/bin/pwpolicy"
|
|
46
|
+
STORE="node $AGENT_HOME/.instar/scripts/credential-store.mjs"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
HOST_LABEL="$(scutil --get ComputerName 2>/dev/null || hostname)"
|
|
50
|
+
echo "Plan: create standard user '$PROOF_USER' on $HOST_LABEL; password generated here, stored in the vault of $AGENT_HOME as '$VAULT_KEY' (and Bitwarden) before the user exists; account policies cleared (no forced change at first login)."
|
|
51
|
+
if [ "$EXECUTE" -ne 1 ]; then
|
|
52
|
+
echo "Dry run: nothing done. Add --execute on the proof Mac to do it."
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
if [ -z "${LOCKED_PROOF_FAKE_BIN:-}" ]; then
|
|
57
|
+
[ "$(uname -s)" = "Darwin" ] || { echo "macOS only" >&2; exit 1; }
|
|
58
|
+
sudo -n true 2>/dev/null || { echo "needs passwordless sudo" >&2; exit 1; }
|
|
59
|
+
fi
|
|
60
|
+
if $DSCL . -read "/Users/$PROOF_USER" UniqueID >/dev/null 2>&1; then
|
|
61
|
+
echo "user $PROOF_USER already exists; remove it first (remove-proof-user.sh)" >&2
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
HELPER="$AGENT_HOME/.instar/scripts/credential-store.mjs"
|
|
66
|
+
if [ -z "${LOCKED_PROOF_FAKE_BIN:-}" ] && [ ! -f "$HELPER" ]; then echo "no $HELPER" >&2; exit 1; fi
|
|
67
|
+
if [ -f "$HELPER" ]; then
|
|
68
|
+
if grep -Eq "bw\(\['(create', 'item'|edit', 'item', [a-z.]+), bw\(" "$HELPER"; then
|
|
69
|
+
echo "refusing: $HELPER puts the encoded Bitwarden item on bw's command line; update it to pass it on stdin first" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
PW="$(node --input-type=module -e "import { generateProofPassword } from '$HERE/lib.mjs'; process.stdout.write(generateProofPassword());")"
|
|
75
|
+
[ "${#PW}" -eq 20 ] || { echo "password generation failed" >&2; exit 1; }
|
|
76
|
+
|
|
77
|
+
FORCE_ARG=""
|
|
78
|
+
[ "$REPLACE" -eq 1 ] && FORCE_ARG="--force"
|
|
79
|
+
# printf is a shell builtin: the value is never an argument of any process.
|
|
80
|
+
printf '%s' "$PW" | (cd "$AGENT_HOME" && $STORE "$VAULT_KEY" --bw-name "Instar locked-proof test user ($HOST_LABEL)" \
|
|
81
|
+
--bw-user "$PROOF_USER" --bw-note "Throwaway macOS user for the locked-screen proof run. Delete with the user." $FORCE_ARG)
|
|
82
|
+
echo "stored in the vault as $VAULT_KEY"
|
|
83
|
+
|
|
84
|
+
printf '%s' "$PW" | $SUDO python3 "$HERE/pty-feed.py" --prompts 2 --timeout 120 -- \
|
|
85
|
+
$SYSADMINCTL -addUser "$PROOF_USER" -fullName "Instar proof" -password -
|
|
86
|
+
$SUDO $PWPOLICY -u "$PROOF_USER" -clearaccountpolicies >/dev/null
|
|
87
|
+
printf '%s' "$PW" | $SUDO python3 "$HERE/pty-feed.py" --prompts 1 --timeout 30 -- $DSCL . -authonly "$PROOF_USER"
|
|
88
|
+
unset PW
|
|
89
|
+
echo "created $PROOF_USER; the stored password authenticates"
|