@raidiant/notifai 10.1.1 → 10.1.3
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/CHANGELOG.md +35 -0
- package/dist/commands-core.d.ts +4 -1
- package/dist/commands-core.js.map +1 -1
- package/dist/commands-doctor.js +2 -7
- package/dist/commands-doctor.js.map +1 -1
- package/dist/commands-hooks.d.ts +12 -0
- package/dist/commands-hooks.js +15 -1
- package/dist/commands-hooks.js.map +1 -1
- package/dist/commands-init.js +9 -6
- package/dist/commands-init.js.map +1 -1
- package/dist/commands-send-support.js +10 -11
- package/dist/commands-send-support.js.map +1 -1
- package/dist/commands-skill.js +72 -54
- package/dist/commands-skill.js.map +1 -1
- package/dist/harness-session-title.d.ts +22 -0
- package/dist/harness-session-title.js +74 -0
- package/dist/harness-session-title.js.map +1 -0
- package/dist/invocation-context.d.ts +1 -0
- package/dist/invocation-context.js +15 -1
- package/dist/invocation-context.js.map +1 -1
- package/dist/native-skills.d.ts +13 -3
- package/dist/native-skills.js +51 -12
- package/dist/native-skills.js.map +1 -1
- package/dist/node-floor.d.ts +2 -1
- package/dist/node-floor.js +9 -5
- package/dist/node-floor.js.map +1 -1
- package/dist/orca-session-title.d.ts +5 -4
- package/dist/orca-session-title.js +32 -19
- package/dist/orca-session-title.js.map +1 -1
- package/dist/platform.d.ts +11 -17
- package/dist/platform.js +20 -23
- package/dist/platform.js.map +1 -1
- package/dist/release.d.ts +5 -5
- package/dist/release.js +5 -5
- package/dist/session-labels.d.ts +2 -0
- package/dist/session-labels.js +20 -9
- package/dist/session-labels.js.map +1 -1
- package/dist/setup-destinations.js +1 -1
- package/dist/setup-destinations.js.map +1 -1
- package/dist/skill-integrity.d.ts +49 -0
- package/dist/skill-integrity.js +154 -0
- package/dist/skill-integrity.js.map +1 -0
- package/dist/skill-source/manifest.json +16 -0
- package/dist/skill-source/notifai/SKILL.md +390 -0
- package/dist/skill-source/notifai/references/harness-setup.md +220 -0
- package/package.json +11 -14
- package/src/atomic-file.ts +0 -198
- package/src/claude-wake.test.ts +0 -321
- package/src/claude-wake.ts +0 -499
- package/src/cli-bin.test.ts +0 -176
- package/src/cli-bin.ts +0 -233
- package/src/cli-release.test.ts +0 -46
- package/src/cli-release.ts +0 -78
- package/src/cli-update-contract.ts +0 -9
- package/src/cli-update.test.ts +0 -125
- package/src/client.test.ts +0 -390
- package/src/client.ts +0 -352
- package/src/codex-wake.test.ts +0 -254
- package/src/codex-wake.ts +0 -301
- package/src/command-session.ts +0 -38
- package/src/commands-agent-sessions.ts +0 -72
- package/src/commands-auth.ts +0 -238
- package/src/commands-config.ts +0 -336
- package/src/commands-core.ts +0 -312
- package/src/commands-devices.ts +0 -180
- package/src/commands-doctor.ts +0 -1501
- package/src/commands-guidance.ts +0 -181
- package/src/commands-harness-context.ts +0 -207
- package/src/commands-hook-shape.ts +0 -69
- package/src/commands-hooks.ts +0 -2318
- package/src/commands-init.ts +0 -1055
- package/src/commands-io.ts +0 -108
- package/src/commands-logs.ts +0 -243
- package/src/commands-project.ts +0 -47
- package/src/commands-send-support.test.ts +0 -156
- package/src/commands-send-support.ts +0 -311
- package/src/commands-send.ts +0 -875
- package/src/commands-setup-proof.test.ts +0 -72
- package/src/commands-setup-proof.ts +0 -124
- package/src/commands-skill.test.ts +0 -44
- package/src/commands-skill.ts +0 -204
- package/src/commands-sounds.test.ts +0 -106
- package/src/commands-sounds.ts +0 -49
- package/src/commands-update.ts +0 -180
- package/src/commands.test.ts +0 -8297
- package/src/commands.ts +0 -77
- package/src/config-schema.test.ts +0 -153
- package/src/config-schema.ts +0 -349
- package/src/config.test.ts +0 -581
- package/src/config.ts +0 -475
- package/src/credentials.test.ts +0 -403
- package/src/credentials.ts +0 -403
- package/src/file-lock-process-worker.test.ts +0 -64
- package/src/file-lock.test.ts +0 -320
- package/src/file-lock.ts +0 -393
- package/src/fixtures/hermes-0.20.6-classic-cli-local.json +0 -50
- package/src/fixtures/openclaw-2026.7.1-2-agent-local.json +0 -57
- package/src/generated-session-label.ts +0 -155
- package/src/guidance-content.ts +0 -278
- package/src/guidance-render.ts +0 -56
- package/src/guidance.test.ts +0 -327
- package/src/guidance.ts +0 -233
- package/src/harness-integration.test.ts +0 -294
- package/src/harnesses.test.ts +0 -149
- package/src/harnesses.ts +0 -164
- package/src/hook-adapter.test.ts +0 -496
- package/src/hook-adapter.ts +0 -424
- package/src/hook-input.test.ts +0 -39
- package/src/hook-input.ts +0 -72
- package/src/hooks.test.ts +0 -5824
- package/src/hooks.ts +0 -4011
- package/src/install-hooks.test.ts +0 -1614
- package/src/install-hooks.ts +0 -1501
- package/src/interactive.test.ts +0 -387
- package/src/interactive.ts +0 -858
- package/src/invocation-context.test.ts +0 -352
- package/src/invocation-context.ts +0 -197
- package/src/logging-process-worker.test.ts +0 -45
- package/src/logging.test.ts +0 -544
- package/src/logging.ts +0 -698
- package/src/main-run.ts +0 -45
- package/src/main.ts +0 -12
- package/src/native-skills.test.ts +0 -115
- package/src/native-skills.ts +0 -162
- package/src/node-floor.test.ts +0 -27
- package/src/node-floor.ts +0 -37
- package/src/onboarding-docs.test.ts +0 -25
- package/src/openclaw-plugin.ts +0 -409
- package/src/opencode-plugin.ts +0 -298
- package/src/orca-session-title.test.ts +0 -54
- package/src/orca-session-title.ts +0 -103
- package/src/pack-feedback-slice.test.ts +0 -212
- package/src/pack-feedback-slice.ts +0 -187
- package/src/platform.test.ts +0 -152
- package/src/platform.ts +0 -173
- package/src/program.test.ts +0 -317
- package/src/program.ts +0 -842
- package/src/project-enablement.test.ts +0 -55
- package/src/project-enablement.ts +0 -99
- package/src/question-caps.test.ts +0 -49
- package/src/question-settlement-process.ts +0 -47
- package/src/question-timing.ts +0 -56
- package/src/readiness.test.ts +0 -25
- package/src/readiness.ts +0 -212
- package/src/release.ts +0 -51
- package/src/send-attempts.test.ts +0 -143
- package/src/send-attempts.ts +0 -188
- package/src/send.test.ts +0 -28
- package/src/send.ts +0 -436
- package/src/session-activation.ts +0 -61
- package/src/session-end-log-lock-worker.test.ts +0 -35
- package/src/session-labels.test.ts +0 -490
- package/src/session-labels.ts +0 -474
- package/src/setup-destinations.test.ts +0 -41
- package/src/setup-destinations.ts +0 -50
- package/src/skill.test.ts +0 -426
- package/src/sound-ref.test.ts +0 -31
- package/src/sound-ref.ts +0 -34
- package/src/trust-policy.test.ts +0 -701
- package/src/ui/banner.ts +0 -95
- package/src/ui/config-view.test.ts +0 -90
- package/src/ui/config-view.ts +0 -138
- package/src/ui/help.test.ts +0 -16
- package/src/ui/help.ts +0 -128
- package/src/ui/theme.test.ts +0 -92
- package/src/ui/theme.ts +0 -265
- package/src/url-policy.ts +0 -399
- package/tsconfig.json +0 -10
package/src/main-run.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Everything the CLI needs to actually run a command.
|
|
3
|
-
*
|
|
4
|
-
* Separate from `main.ts` so that the Node floor check there can decide
|
|
5
|
-
* whether this file is safe to parse at all: a static import would be hoisted
|
|
6
|
-
* above the check and fail first on the very runtimes the check exists to
|
|
7
|
-
* turn away.
|
|
8
|
-
*/
|
|
9
|
-
import { realIo, type CommandDeps } from './commands.js'
|
|
10
|
-
import { defaultCredentialStore } from './credentials.js'
|
|
11
|
-
import { nativeSkills } from './native-skills.js'
|
|
12
|
-
import { argvFlagNames, bootstrapLogger } from './logging.js'
|
|
13
|
-
import { buildProgram } from './program.js'
|
|
14
|
-
import { spawnQuestionSettlement } from './question-settlement-process.js'
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* The local record for this invocation.
|
|
18
|
-
*
|
|
19
|
-
* Built before the command tree so that the very first thing recorded is the
|
|
20
|
-
* command starting — including for a command that goes on to fail before it has
|
|
21
|
-
* resolved anything. It configures itself from disk and disables itself if it
|
|
22
|
-
* cannot write, so nothing below has to handle it failing.
|
|
23
|
-
*/
|
|
24
|
-
const logger = bootstrapLogger()
|
|
25
|
-
|
|
26
|
-
const deps: CommandDeps = {
|
|
27
|
-
io: realIo(),
|
|
28
|
-
store: defaultCredentialStore(),
|
|
29
|
-
env: process.env,
|
|
30
|
-
cwd: process.cwd(),
|
|
31
|
-
nativeSkills,
|
|
32
|
-
spawnQuestionSettlement,
|
|
33
|
-
logger,
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const startedAt = Date.now()
|
|
37
|
-
process.on('exit', (code) => {
|
|
38
|
-
logger.info('cli.end', {
|
|
39
|
-
exit: code,
|
|
40
|
-
duration_ms: Date.now() - startedAt,
|
|
41
|
-
flags: argvFlagNames(process.argv.slice(2)),
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
await buildProgram(deps).parseAsync(process.argv)
|
package/src/main.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { belowNodeFloor, nodeFloorMessage } from './node-floor.js'
|
|
3
|
-
|
|
4
|
-
// Before anything else is parsed. `node-floor.js` imports nothing and uses no
|
|
5
|
-
// syntax newer than the floor it checks, so it can report on the runtimes that
|
|
6
|
-
// would otherwise fail somewhere in a dependency with no mention of Notifai.
|
|
7
|
-
if (belowNodeFloor(process.version)) {
|
|
8
|
-
for (const line of nodeFloorMessage(process.version)) process.stderr.write(`${line}\n`)
|
|
9
|
-
process.exit(2)
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
await import('./main-run.js')
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs'
|
|
2
|
-
import os from 'node:os'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import { describe, expect, it } from 'vitest'
|
|
5
|
-
import { SKILLS_INSTALLER_SPEC, nativeSkills, skillsAddArgv, skillsRemoveArgv } from './native-skills.js'
|
|
6
|
-
|
|
7
|
-
function writeLock(file: string, ref: string): void {
|
|
8
|
-
mkdirSync(path.dirname(file), { recursive: true })
|
|
9
|
-
writeFileSync(
|
|
10
|
-
file,
|
|
11
|
-
`${JSON.stringify({
|
|
12
|
-
version: 1,
|
|
13
|
-
skills: {
|
|
14
|
-
notifai: {
|
|
15
|
-
source: 'Raidiant-io/notifai',
|
|
16
|
-
sourceType: 'github',
|
|
17
|
-
sourceUrl: 'https://github.com/Raidiant-io/notifai.git',
|
|
18
|
-
ref,
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
})}\n`,
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe('skillsAddArgv', () => {
|
|
26
|
-
it('pins the installer package to an exact reviewed version', () => {
|
|
27
|
-
const argv = skillsAddArgv({
|
|
28
|
-
source: 'Raidiant-io/notifai#v8.0.0',
|
|
29
|
-
skill: 'notifai',
|
|
30
|
-
cwd: '/tmp',
|
|
31
|
-
env: {},
|
|
32
|
-
})
|
|
33
|
-
expect(SKILLS_INSTALLER_SPEC).toMatch(/^skills@\d+\.\d+\.\d+$/)
|
|
34
|
-
expect(argv).toEqual([
|
|
35
|
-
'-y',
|
|
36
|
-
SKILLS_INSTALLER_SPEC,
|
|
37
|
-
'add',
|
|
38
|
-
'Raidiant-io/notifai#v8.0.0',
|
|
39
|
-
'--skill',
|
|
40
|
-
'notifai',
|
|
41
|
-
])
|
|
42
|
-
expect(argv).not.toContain('skills')
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('uninstalls one skill in the named scope without a prompt', () => {
|
|
46
|
-
expect(
|
|
47
|
-
skillsRemoveArgv({
|
|
48
|
-
skill: 'notifai',
|
|
49
|
-
scope: 'project',
|
|
50
|
-
cwd: '/tmp',
|
|
51
|
-
env: {},
|
|
52
|
-
}),
|
|
53
|
-
).toEqual(['-y', SKILLS_INSTALLER_SPEC, 'remove', 'notifai', '--yes'])
|
|
54
|
-
expect(
|
|
55
|
-
skillsRemoveArgv({
|
|
56
|
-
skill: 'notifai',
|
|
57
|
-
scope: 'global',
|
|
58
|
-
cwd: '/tmp',
|
|
59
|
-
env: {},
|
|
60
|
-
}),
|
|
61
|
-
).toEqual(['-y', SKILLS_INSTALLER_SPEC, 'remove', 'notifai', '--global', '--yes'])
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
describe('nativeSkills.list', () => {
|
|
66
|
-
it('reads the project lock file instead of spawning npx', async () => {
|
|
67
|
-
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-skills-project-'))
|
|
68
|
-
writeLock(path.join(cwd, 'skills-lock.json'), 'v0.5.1')
|
|
69
|
-
|
|
70
|
-
const result = await nativeSkills.list('project', cwd, { PATH: '/nonexistent' })
|
|
71
|
-
expect(result.error).toBeUndefined()
|
|
72
|
-
expect(result.skills).toEqual([
|
|
73
|
-
expect.objectContaining({
|
|
74
|
-
name: 'notifai',
|
|
75
|
-
scope: 'project',
|
|
76
|
-
source: 'Raidiant-io/notifai',
|
|
77
|
-
sourceType: 'github',
|
|
78
|
-
ref: 'v0.5.1',
|
|
79
|
-
}),
|
|
80
|
-
])
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it('reads the XDG global lock file instead of spawning npx', async () => {
|
|
84
|
-
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-skills-xdg-'))
|
|
85
|
-
const state = path.join(cwd, 'state')
|
|
86
|
-
writeLock(path.join(state, 'skills', '.skill-lock.json'), 'v0.4.0')
|
|
87
|
-
|
|
88
|
-
const result = await nativeSkills.list('global', cwd, {
|
|
89
|
-
PATH: '/nonexistent',
|
|
90
|
-
XDG_STATE_HOME: state,
|
|
91
|
-
})
|
|
92
|
-
expect(result.error).toBeUndefined()
|
|
93
|
-
expect(result.skills).toEqual([
|
|
94
|
-
expect.objectContaining({ name: 'notifai', scope: 'global', ref: 'v0.4.0' }),
|
|
95
|
-
])
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('falls back to the home-directory global lock', async () => {
|
|
99
|
-
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-skills-home-'))
|
|
100
|
-
const home = path.join(cwd, 'home')
|
|
101
|
-
writeLock(path.join(home, '.agents', '.skill-lock.json'), 'v0.3.0')
|
|
102
|
-
|
|
103
|
-
const result = await nativeSkills.list('global', cwd, { PATH: '/nonexistent', HOME: home })
|
|
104
|
-
expect(result.error).toBeUndefined()
|
|
105
|
-
expect(result.skills).toEqual([
|
|
106
|
-
expect.objectContaining({ name: 'notifai', scope: 'global', ref: 'v0.3.0' }),
|
|
107
|
-
])
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('treats a missing lock as an empty inventory, not an installer error', async () => {
|
|
111
|
-
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-skills-missing-'))
|
|
112
|
-
const result = await nativeSkills.list('project', cwd, { PATH: '/nonexistent' })
|
|
113
|
-
expect(result).toEqual({ skills: [] })
|
|
114
|
-
})
|
|
115
|
-
})
|
package/src/native-skills.ts
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process'
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import { accountHome, npxLaunch } from './platform.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Exact reviewed version of the external `skills` installer.
|
|
8
|
-
*
|
|
9
|
-
* The GitHub skill source stays the immutable CLI version tag. The installer
|
|
10
|
-
* program that fetches it must not float on `latest`.
|
|
11
|
-
*/
|
|
12
|
-
export const SKILLS_INSTALLER_SPEC = 'skills@1.5.23'
|
|
13
|
-
|
|
14
|
-
/** The two scopes offered by the skills installer. */
|
|
15
|
-
export type SkillScope = 'project' | 'global'
|
|
16
|
-
|
|
17
|
-
/** The installer-managed evidence needed by Notifai readiness. */
|
|
18
|
-
export interface NativeSkill {
|
|
19
|
-
name: string
|
|
20
|
-
scope: SkillScope
|
|
21
|
-
path: string
|
|
22
|
-
source: string | null
|
|
23
|
-
sourceType: string | null
|
|
24
|
-
sourceUrl: string | null
|
|
25
|
-
ref: string | null
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface SkillsListResult {
|
|
29
|
-
skills: NativeSkill[]
|
|
30
|
-
error?: string
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface SkillsAddOptions {
|
|
34
|
-
source: string
|
|
35
|
-
skill: string
|
|
36
|
-
scope?: SkillScope
|
|
37
|
-
cwd: string
|
|
38
|
-
env: NodeJS.ProcessEnv
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface SkillsRemoveOptions {
|
|
42
|
-
skill: string
|
|
43
|
-
scope: SkillScope
|
|
44
|
-
cwd: string
|
|
45
|
-
env: NodeJS.ProcessEnv
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface NativeSkills {
|
|
49
|
-
/** Launch the native interactive `npx skills add` flow. */
|
|
50
|
-
add(options: SkillsAddOptions): Promise<number>
|
|
51
|
-
/** Uninstall one installer-managed skill in one scope. */
|
|
52
|
-
remove(options: SkillsRemoveOptions): Promise<number>
|
|
53
|
-
/** Read installer-managed inventory from lock files. Does not spawn npx. */
|
|
54
|
-
list(scope: SkillScope, cwd: string, env: NodeJS.ProcessEnv): Promise<SkillsListResult>
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
interface LockEntry {
|
|
58
|
-
source?: unknown
|
|
59
|
-
sourceType?: unknown
|
|
60
|
-
sourceUrl?: unknown
|
|
61
|
-
ref?: unknown
|
|
62
|
-
skillPath?: unknown
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
interface LockFile {
|
|
66
|
-
skills?: Record<string, LockEntry>
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function skillLockPath(scope: SkillScope, cwd: string, env: NodeJS.ProcessEnv): string {
|
|
70
|
-
if (scope === 'project') return path.join(cwd, 'skills-lock.json')
|
|
71
|
-
const stateHome = env['XDG_STATE_HOME']
|
|
72
|
-
return stateHome !== undefined && stateHome !== ''
|
|
73
|
-
? path.join(stateHome, 'skills', '.skill-lock.json')
|
|
74
|
-
: path.join(accountHome(env), '.agents', '.skill-lock.json')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function readLock(scope: SkillScope, cwd: string, env: NodeJS.ProcessEnv): LockFile {
|
|
78
|
-
const file = skillLockPath(scope, cwd, env)
|
|
79
|
-
if (!existsSync(file)) return {}
|
|
80
|
-
try {
|
|
81
|
-
const parsed = JSON.parse(readFileSync(file, 'utf8')) as LockFile
|
|
82
|
-
return parsed && typeof parsed === 'object' ? parsed : {}
|
|
83
|
-
} catch {
|
|
84
|
-
return {}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function conventionalSkillPath(
|
|
89
|
-
scope: SkillScope,
|
|
90
|
-
name: string,
|
|
91
|
-
cwd: string,
|
|
92
|
-
env: NodeJS.ProcessEnv,
|
|
93
|
-
): string {
|
|
94
|
-
if (scope === 'project') return path.join(cwd, '.agents', 'skills', name)
|
|
95
|
-
return path.join(accountHome(env), '.agents', 'skills', name)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function skillsFromLock(scope: SkillScope, cwd: string, env: NodeJS.ProcessEnv): NativeSkill[] {
|
|
99
|
-
const lock = readLock(scope, cwd, env)
|
|
100
|
-
return Object.entries(lock.skills ?? {}).flatMap(([name, entry]): NativeSkill[] => {
|
|
101
|
-
if (entry === null || typeof entry !== 'object') return []
|
|
102
|
-
return [
|
|
103
|
-
{
|
|
104
|
-
name,
|
|
105
|
-
scope,
|
|
106
|
-
path:
|
|
107
|
-
typeof entry.skillPath === 'string' && entry.skillPath !== ''
|
|
108
|
-
? entry.skillPath
|
|
109
|
-
: conventionalSkillPath(scope, name, cwd, env),
|
|
110
|
-
source: typeof entry.source === 'string' ? entry.source : null,
|
|
111
|
-
sourceType: typeof entry.sourceType === 'string' ? entry.sourceType : null,
|
|
112
|
-
sourceUrl: typeof entry.sourceUrl === 'string' ? entry.sourceUrl : null,
|
|
113
|
-
ref: typeof entry.ref === 'string' ? entry.ref : null,
|
|
114
|
-
},
|
|
115
|
-
]
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function run(args: string[], options: { cwd: string; env: NodeJS.ProcessEnv }): Promise<number> {
|
|
120
|
-
return new Promise((resolve) => {
|
|
121
|
-
const launch = npxLaunch(args, options)
|
|
122
|
-
const child = spawn(launch.file, launch.args, launch.options)
|
|
123
|
-
child.on('error', () => resolve(1))
|
|
124
|
-
child.on('exit', (code) => resolve(code ?? 1))
|
|
125
|
-
})
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** argv for `npx`, including the pinned installer spec. */
|
|
129
|
-
export function skillsAddArgv(options: SkillsAddOptions): string[] {
|
|
130
|
-
const args = ['-y', SKILLS_INSTALLER_SPEC, 'add', options.source, '--skill', options.skill]
|
|
131
|
-
if (options.scope === 'global') args.push('--global')
|
|
132
|
-
// An explicit scope is the unattended contract. Native `--yes` keeps all
|
|
133
|
-
// remaining installer prompts non-interactive after the scope is chosen.
|
|
134
|
-
if (options.scope !== undefined) args.push('--yes')
|
|
135
|
-
return args
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** argv for uninstalling one skill in one installer scope. */
|
|
139
|
-
export function skillsRemoveArgv(options: SkillsRemoveOptions): string[] {
|
|
140
|
-
const args = ['-y', SKILLS_INSTALLER_SPEC, 'remove', options.skill]
|
|
141
|
-
if (options.scope === 'global') args.push('--global')
|
|
142
|
-
args.push('--yes')
|
|
143
|
-
return args
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** The only process/filesystem adapter Notifai needs for the external installer. */
|
|
147
|
-
export const nativeSkills: NativeSkills = {
|
|
148
|
-
async add(options) {
|
|
149
|
-
return run(skillsAddArgv(options), { cwd: options.cwd, env: options.env })
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
async remove(options) {
|
|
153
|
-
return run(skillsRemoveArgv(options), { cwd: options.cwd, env: options.env })
|
|
154
|
-
},
|
|
155
|
-
|
|
156
|
-
async list(scope, cwd, env) {
|
|
157
|
-
// Presence is already on disk. `npx skills list` takes seconds and cannot
|
|
158
|
-
// change whether the notifai skill is installed — the lock file is what
|
|
159
|
-
// the installer itself consults.
|
|
160
|
-
return { skills: skillsFromLock(scope, cwd, env) }
|
|
161
|
-
},
|
|
162
|
-
}
|
package/src/node-floor.test.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { NODE_MAJOR_FLOOR, belowNodeFloor, nodeFloorMessage, nodeMajor } from './node-floor.js'
|
|
3
|
-
|
|
4
|
-
describe('the Node runtime floor', () => {
|
|
5
|
-
it('turns away a runtime below the floor', () => {
|
|
6
|
-
expect(belowNodeFloor('v20.11.1')).toBe(true)
|
|
7
|
-
expect(belowNodeFloor('v18.0.0')).toBe(true)
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
it('admits the floor and everything above it', () => {
|
|
11
|
-
expect(belowNodeFloor(`v${NODE_MAJOR_FLOOR}.0.0`)).toBe(false)
|
|
12
|
-
expect(belowNodeFloor('v24.3.0')).toBe(false)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('does not strand a reader whose version it cannot read', () => {
|
|
16
|
-
expect(nodeMajor('bun-1.2')).toBeNull()
|
|
17
|
-
expect(belowNodeFloor('bun-1.2')).toBe(false)
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('says what is wrong and exactly one thing to do', () => {
|
|
21
|
-
const [problem, next] = nodeFloorMessage('v20.11.1')
|
|
22
|
-
expect(problem).toContain('Node 22 or newer')
|
|
23
|
-
expect(problem).toContain('v20.11.1')
|
|
24
|
-
expect(next).toMatch(/^next: /)
|
|
25
|
-
expect(next).toContain('nodejs.org')
|
|
26
|
-
})
|
|
27
|
-
})
|
package/src/node-floor.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Node version this CLI needs, asserted before anything else loads.
|
|
3
|
-
*
|
|
4
|
-
* `engines` in package.json is advisory: npm prints a warning at install time
|
|
5
|
-
* and runs the binary anyway. So a reader on an older Node met a parser or
|
|
6
|
-
* module error naming a file they did not write, with no mention of Notifai,
|
|
7
|
-
* of Node, or of anything to do about it.
|
|
8
|
-
*
|
|
9
|
-
* This module is deliberately plain — no imports, no syntax newer than the
|
|
10
|
-
* floor it is checking — so it can run and report on the runtimes it exists to
|
|
11
|
-
* turn away. Everything else is loaded after it passes.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export const NODE_MAJOR_FLOOR = 22
|
|
15
|
-
|
|
16
|
-
/** The major from a `process.version` string, or null when unrecognisable. */
|
|
17
|
-
export function nodeMajor(version: string): number | null {
|
|
18
|
-
const match = /^v?(\d+)\./.exec(version)
|
|
19
|
-
if (match === null) return null
|
|
20
|
-
const major = Number(match[1])
|
|
21
|
-
return Number.isFinite(major) ? major : null
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function belowNodeFloor(version: string): boolean {
|
|
25
|
-
const major = nodeMajor(version)
|
|
26
|
-
// An unrecognisable version is not evidence of an old runtime, and refusing
|
|
27
|
-
// to run on one would strand a reader whose Node is fine.
|
|
28
|
-
return major !== null && major < NODE_MAJOR_FLOOR
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** One sentence for what is wrong, one for what to do about it. */
|
|
32
|
-
export function nodeFloorMessage(version: string): string[] {
|
|
33
|
-
return [
|
|
34
|
-
`Notifai needs Node ${NODE_MAJOR_FLOOR} or newer; this is Node ${version}.`,
|
|
35
|
-
`next: install Node ${NODE_MAJOR_FLOOR}+ (https://nodejs.org), then run this again.`,
|
|
36
|
-
]
|
|
37
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
|
-
import { describe, expect, it } from 'vitest'
|
|
3
|
-
|
|
4
|
-
const publicReadme = readFileSync(new URL('../../../README.md', import.meta.url), 'utf8')
|
|
5
|
-
const cliReadme = readFileSync(new URL('../README.md', import.meta.url), 'utf8')
|
|
6
|
-
|
|
7
|
-
describe('public onboarding docs', () => {
|
|
8
|
-
it('installs the CLI before the first quick-start invocation', () => {
|
|
9
|
-
const using = publicReadme.slice(publicReadme.indexOf('## Using it'))
|
|
10
|
-
const install = using.indexOf('npm install -g @raidiant/notifai')
|
|
11
|
-
const firstInvocation = using.search(/^notifai(?:\s|$)/m)
|
|
12
|
-
|
|
13
|
-
expect(install).toBeGreaterThan(0)
|
|
14
|
-
expect(firstInvocation).toBeGreaterThan(install)
|
|
15
|
-
expect(using.slice(install, firstInvocation)).toContain('@raidiant/notifai')
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('states the active Android external-test boundary without promising a public release', () => {
|
|
19
|
-
const harnesses = cliReadme.slice(cliReadme.indexOf('## Agent harnesses'))
|
|
20
|
-
|
|
21
|
-
expect(harnesses).toMatch(/Android Companion App is active[\s\S]*external testing/i)
|
|
22
|
-
expect(harnesses).toMatch(/Firebase App Distribution/i)
|
|
23
|
-
expect(harnesses).toMatch(/not a\s+public release or a Google Play promise/i)
|
|
24
|
-
})
|
|
25
|
-
})
|