pinokiod 7.3.1 → 7.3.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.
Files changed (122) hide show
  1. package/kernel/api/github/index.js +444 -0
  2. package/kernel/api/index.js +199 -11
  3. package/kernel/api/process/index.js +124 -44
  4. package/kernel/api/shell_run_template.js +273 -0
  5. package/kernel/api/uri/index.js +51 -0
  6. package/kernel/bin/git.js +9 -10
  7. package/kernel/bin/huggingface.js +1 -1
  8. package/kernel/bin/zip.js +9 -1
  9. package/kernel/connect/providers/github/README.md +5 -4
  10. package/kernel/environment.js +195 -92
  11. package/kernel/git.js +98 -19
  12. package/kernel/gitconfig_template +7 -0
  13. package/kernel/gpu/amd.js +72 -0
  14. package/kernel/gpu/apple.js +8 -0
  15. package/kernel/gpu/common.js +12 -0
  16. package/kernel/gpu/intel.js +47 -0
  17. package/kernel/gpu/nvidia.js +8 -0
  18. package/kernel/index.js +11 -1
  19. package/kernel/managed_skills.js +871 -0
  20. package/kernel/plugin.js +6 -58
  21. package/kernel/plugin_sources.js +316 -0
  22. package/kernel/resource_usage/gpu.js +349 -0
  23. package/kernel/resource_usage/index.js +322 -0
  24. package/kernel/resource_usage/macos_footprint.js +197 -0
  25. package/kernel/resource_usage/preferences.js +92 -0
  26. package/kernel/resource_usage/process_tree.js +303 -0
  27. package/kernel/scripts/git/create +4 -4
  28. package/kernel/scripts/git/fork +7 -8
  29. package/kernel/shell.js +23 -2
  30. package/kernel/shells.js +41 -0
  31. package/kernel/sysinfo.js +62 -9
  32. package/kernel/util.js +60 -0
  33. package/package.json +1 -1
  34. package/server/index.js +984 -156
  35. package/server/lib/app_log_report.js +543 -0
  36. package/server/lib/content_validation.js +55 -33
  37. package/server/lib/launcher_instruction_bootstrap.js +4 -96
  38. package/server/lib/terminal_session_helpers.js +0 -3
  39. package/server/public/common.js +77 -31
  40. package/server/public/create-launcher.js +4 -32
  41. package/server/public/logs.js +1428 -0
  42. package/server/public/nav.js +7 -0
  43. package/server/public/plugin-detail.js +93 -10
  44. package/server/public/privacy_filter_worker.js +391 -0
  45. package/server/public/style.css +1104 -154
  46. package/server/public/task-launcher.js +8 -29
  47. package/server/public/universal-launcher.css +8 -6
  48. package/server/public/universal-launcher.js +3 -27
  49. package/server/routes/apps.js +195 -1
  50. package/server/views/app.ejs +3041 -717
  51. package/server/views/autolaunch.ejs +917 -0
  52. package/server/views/bootstrap.ejs +7 -1
  53. package/server/views/d.ejs +408 -65
  54. package/server/views/editor.ejs +85 -19
  55. package/server/views/index.ejs +661 -111
  56. package/server/views/init/index.ejs +1 -1
  57. package/server/views/install.ejs +1 -1
  58. package/server/views/logs.ejs +164 -86
  59. package/server/views/net.ejs +7 -1
  60. package/server/views/partials/d_terminal_column.ejs +2 -2
  61. package/server/views/partials/d_terminal_options.ejs +0 -8
  62. package/server/views/partials/fs_status.ejs +47 -0
  63. package/server/views/partials/home_action_modal.ejs +86 -0
  64. package/server/views/partials/home_run_menu.ejs +87 -0
  65. package/server/views/partials/main_sidebar.ejs +2 -0
  66. package/server/views/partials/menu.ejs +1 -1
  67. package/server/views/plugin_detail.ejs +19 -4
  68. package/server/views/plugins.ejs +201 -3
  69. package/server/views/pre.ejs +1 -1
  70. package/server/views/pro.ejs +1 -1
  71. package/server/views/shell.ejs +40 -18
  72. package/server/views/skills.ejs +506 -0
  73. package/server/views/terminal.ejs +45 -19
  74. package/spec/INSTRUCTION_SYNC.md +20 -10
  75. package/system/plugin/antigravity-cli/antigravity.png +0 -0
  76. package/system/plugin/antigravity-cli/common.js +155 -0
  77. package/system/plugin/antigravity-cli/install.js +272 -0
  78. package/system/plugin/antigravity-cli/pinokio.js +13 -0
  79. package/system/plugin/antigravity-cli-auto/antigravity.png +0 -0
  80. package/system/plugin/antigravity-cli-auto/pinokio.js +13 -0
  81. package/system/plugin/claude/claude.png +0 -0
  82. package/system/plugin/claude/pinokio.js +47 -0
  83. package/system/plugin/claude-auto/claude.png +0 -0
  84. package/system/plugin/claude-auto/pinokio.js +58 -0
  85. package/system/plugin/claude-desktop/icon.jpeg +0 -0
  86. package/system/plugin/claude-desktop/pinokio.js +23 -0
  87. package/system/plugin/codex/openai.webp +0 -0
  88. package/system/plugin/codex/pinokio.js +42 -0
  89. package/system/plugin/codex-auto/openai.webp +0 -0
  90. package/system/plugin/codex-auto/pinokio.js +49 -0
  91. package/system/plugin/codex-desktop/icon.png +0 -0
  92. package/system/plugin/codex-desktop/pinokio.js +23 -0
  93. package/system/plugin/crush/crush.png +0 -0
  94. package/system/plugin/crush/pinokio.js +15 -0
  95. package/system/plugin/cursor/cursor.jpeg +0 -0
  96. package/system/plugin/cursor/pinokio.js +23 -0
  97. package/system/plugin/qwen/pinokio.js +34 -0
  98. package/system/plugin/qwen/qwen.png +0 -0
  99. package/system/plugin/vscode/pinokio.js +20 -0
  100. package/system/plugin/vscode/vscode.png +0 -0
  101. package/system/plugin/windsurf/pinokio.js +23 -0
  102. package/system/plugin/windsurf/windsurf.png +0 -0
  103. package/test/antigravity-cli-plugin.test.js +185 -0
  104. package/test/app-api.test.js +239 -0
  105. package/test/app-log-report.test.js +67 -0
  106. package/test/environment-cache-preflight.test.js +98 -0
  107. package/test/git-bin.test.js +59 -0
  108. package/test/git-defaults.test.js +97 -0
  109. package/test/github-api.test.js +158 -0
  110. package/test/github-connection.test.js +117 -0
  111. package/test/huggingface-bin.test.js +25 -0
  112. package/test/managed-skills.test.js +351 -0
  113. package/test/plugin-action-functions.test.js +337 -0
  114. package/test/plugin-dev-iframe.test.js +17 -0
  115. package/test/plugin-sources.test.js +203 -0
  116. package/test/privacy-filter-worker-heuristics.test.js +69 -0
  117. package/test/process-wait.test.js +169 -0
  118. package/test/script-api.test.js +97 -0
  119. package/test/shell-api.test.js +134 -0
  120. package/test/shell-run-template.test.js +209 -0
  121. package/test/storage-api.test.js +137 -0
  122. package/test/uri-api.test.js +100 -0
@@ -0,0 +1,98 @@
1
+ const test = require("node:test")
2
+ const assert = require("node:assert/strict")
3
+ const fs = require("node:fs/promises")
4
+ const fssync = require("node:fs")
5
+ const os = require("node:os")
6
+ const path = require("node:path")
7
+ const Environment = require("../kernel/environment")
8
+
9
+ const CACHE_KEYS = ["TMP", "TEMP", "TMPDIR", "PIP_TMPDIR", "UV_CACHE_DIR", "PIP_CACHE_DIR"]
10
+
11
+ const createKernel = (homedir) => ({
12
+ homedir,
13
+ kv: {
14
+ get: async () => null
15
+ },
16
+ exists: (...args) => new Promise((resolve) => {
17
+ fssync.access(path.resolve(homedir, ...args), fssync.constants.F_OK, (error) => {
18
+ resolve(!error)
19
+ })
20
+ })
21
+ })
22
+
23
+ const withTempHome = async (fn) => {
24
+ const homedir = await fs.mkdtemp(path.join(os.tmpdir(), "pinokio-cache-preflight-"))
25
+ try {
26
+ await fs.writeFile(path.join(homedir, "ENVIRONMENT"), "OTHER=value\n")
27
+ await fn(homedir)
28
+ } finally {
29
+ await fs.rm(homedir, { recursive: true, force: true })
30
+ }
31
+ }
32
+
33
+ test("ensurePinokioCacheDirs writes managed cache env defaults and probes directories", async () => {
34
+ await withTempHome(async (homedir) => {
35
+ const kernel = createKernel(homedir)
36
+ const result = await Environment.ensurePinokioCacheDirs(kernel, { throwOnFailure: true })
37
+
38
+ assert.equal(result.errors.length, 0)
39
+ assert.equal(kernel.cacheDirErrors.length, 0)
40
+ assert.equal(kernel.cacheDirPreflight.length, CACHE_KEYS.length)
41
+
42
+ const env = await fs.readFile(path.join(homedir, "ENVIRONMENT"), "utf8")
43
+ for (const key of CACHE_KEYS) {
44
+ assert.match(env, new RegExp(`^${key}=\\.\\/cache\\/${key}$`, "m"))
45
+ const stats = await fs.stat(path.join(homedir, "cache", key))
46
+ assert.equal(stats.isDirectory(), true)
47
+ }
48
+ })
49
+ })
50
+
51
+ test("ensurePinokioCacheDirs repairs managed cache targets that are not writable directories", async () => {
52
+ await withTempHome(async (homedir) => {
53
+ await fs.mkdir(path.join(homedir, "cache"), { recursive: true })
54
+ await fs.writeFile(path.join(homedir, "cache", "TMP"), "not a directory")
55
+
56
+ const kernel = createKernel(homedir)
57
+ const result = await Environment.ensurePinokioCacheDirs(kernel, { throwOnFailure: true })
58
+ const tmpResult = result.results.find((item) => item.key === "TMP")
59
+
60
+ assert.equal(result.errors.length, 0)
61
+ assert.equal(tmpResult.repaired, true)
62
+ const stats = await fs.stat(path.join(homedir, "cache", "TMP"))
63
+ assert.equal(stats.isDirectory(), true)
64
+ })
65
+ })
66
+
67
+ test("requirements ignores malformed pre metadata without breaking launcher checks", async () => {
68
+ await withTempHome(async (homedir) => {
69
+ const appDir = path.join(homedir, "api", "demo")
70
+ await fs.mkdir(appDir, { recursive: true })
71
+ await fs.writeFile(path.join(appDir, "ENVIRONMENT"), "EXISTING=from-app\n")
72
+ const kernel = createKernel(homedir)
73
+
74
+ const missingArray = await Environment.requirements({ pre: "bad" }, appDir, kernel)
75
+ assert.deepEqual(missingArray, {
76
+ items: [],
77
+ requires_instantiation: false
78
+ })
79
+
80
+ const checked = await Environment.requirements({
81
+ pre: [
82
+ null,
83
+ "bad",
84
+ { env: 5 },
85
+ { env: "EXISTING", host: "::bad host::" },
86
+ { key: "MISSING", default: "fallback" }
87
+ ]
88
+ }, appDir, kernel)
89
+
90
+ assert.equal(checked.items.length, 2)
91
+ assert.equal(checked.items[0].env, "EXISTING")
92
+ assert.equal(checked.items[0].host, "")
93
+ assert.equal(checked.items[0].val, "from-app")
94
+ assert.equal(checked.items[1].env, "MISSING")
95
+ assert.equal(checked.items[1].val, "fallback")
96
+ assert.equal(checked.requires_instantiation, true)
97
+ })
98
+ })
@@ -0,0 +1,59 @@
1
+ const assert = require('node:assert/strict')
2
+ const path = require('node:path')
3
+ const test = require('node:test')
4
+
5
+ const GitBin = require('../kernel/bin/git')
6
+
7
+ function createGitBin(platform, packages) {
8
+ const git = new GitBin()
9
+ git.kernel = {
10
+ platform,
11
+ homedir: '/tmp/pinokio',
12
+ path: (...parts) => path.join('/tmp/pinokio', ...parts),
13
+ bin: {
14
+ installed: {
15
+ conda: new Set(packages),
16
+ conda_versions: { git: '2.51.0' },
17
+ },
18
+ },
19
+ }
20
+ return git
21
+ }
22
+
23
+ test('Git bin installs Git Credential Manager with the Git bundle', () => {
24
+ assert.equal(
25
+ createGitBin('darwin', []).cmd(),
26
+ 'git=2.51.0 git-lfs git-credential-manager=2.7.3 gh=2.82.1'
27
+ )
28
+ assert.equal(
29
+ createGitBin('linux', []).cmd(),
30
+ 'git=2.51.0 git-lfs git-credential-manager=2.7.3 gh=2.82.1'
31
+ )
32
+ assert.equal(
33
+ createGitBin('win32', []).cmd(),
34
+ 'git=2.51.0 git-lfs git-credential-manager=2.7.3 gh=2.82.1 m2-base'
35
+ )
36
+ })
37
+
38
+ test('Git bin readiness requires Git Credential Manager and GitHub CLI', async () => {
39
+ assert.equal(
40
+ await createGitBin('darwin', ['git', 'git-lfs']).installed(),
41
+ false
42
+ )
43
+ assert.equal(
44
+ await createGitBin('darwin', ['git', 'git-lfs', 'git-credential-manager']).installed(),
45
+ false
46
+ )
47
+ assert.equal(
48
+ await createGitBin('darwin', ['git', 'git-lfs', 'git-credential-manager', 'gh']).installed(),
49
+ true
50
+ )
51
+ assert.equal(
52
+ await createGitBin('win32', ['git', 'git-lfs', 'git-credential-manager', 'gh']).installed(),
53
+ false
54
+ )
55
+ assert.equal(
56
+ await createGitBin('win32', ['git', 'git-lfs', 'git-credential-manager', 'gh', 'm2-base']).installed(),
57
+ true
58
+ )
59
+ })
@@ -0,0 +1,97 @@
1
+ const assert = require('node:assert/strict')
2
+ const fs = require('node:fs/promises')
3
+ const os = require('node:os')
4
+ const path = require('node:path')
5
+ const test = require('node:test')
6
+ const ini = require('ini')
7
+
8
+ const KernelGit = require('../kernel/git')
9
+
10
+ async function withTempHome(fn) {
11
+ const homedir = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-git-defaults-'))
12
+ try {
13
+ await fn(homedir)
14
+ } finally {
15
+ await fs.rm(homedir, { recursive: true, force: true })
16
+ }
17
+ }
18
+
19
+ function createKernel(homedir) {
20
+ return {
21
+ homedir,
22
+ path: (...parts) => path.resolve(homedir, ...parts),
23
+ }
24
+ }
25
+
26
+ function githubCredentialSection(config) {
27
+ return config['credential "https://github'] && config['credential "https://github']['com"']
28
+ }
29
+
30
+ test('Git defaults configure Git Credential Manager for new Pinokio gitconfig files', async () => {
31
+ await withTempHome(async (homedir) => {
32
+ const git = new KernelGit(createKernel(homedir))
33
+
34
+ await git.ensureDefaults()
35
+
36
+ const config = ini.parse(await fs.readFile(path.join(homedir, 'gitconfig'), 'utf8'))
37
+ assert.equal(config.credential.helper, 'manager')
38
+ assert.equal(config.credential.gitHubAuthModes, 'oauth')
39
+ assert.equal(config.credential.namespace, 'pinokio')
40
+ assert.equal(githubCredentialSection(config).helper, 'manager')
41
+ assert.equal(githubCredentialSection(config).provider, 'github')
42
+ })
43
+ })
44
+
45
+ test('Git defaults migrate the legacy gh GitHub helper to Git Credential Manager', async () => {
46
+ await withTempHome(async (homedir) => {
47
+ await fs.writeFile(path.join(homedir, 'gitconfig'), [
48
+ '[credential "https://github.com"]',
49
+ ' helper = !gh auth git-credential',
50
+ '[user]',
51
+ ' name = custom',
52
+ ' email = custom@example.test',
53
+ '',
54
+ ].join('\n'))
55
+ const git = new KernelGit(createKernel(homedir))
56
+
57
+ await git.ensureDefaults()
58
+
59
+ const config = ini.parse(await fs.readFile(path.join(homedir, 'gitconfig'), 'utf8'))
60
+ assert.equal(config.credential.helper, 'manager')
61
+ assert.equal(config.credential.gitHubAuthModes, 'oauth')
62
+ assert.equal(config.credential.namespace, 'pinokio')
63
+ assert.equal(githubCredentialSection(config).helper, 'manager')
64
+ assert.equal(githubCredentialSection(config).provider, 'github')
65
+ assert.equal(config.user.name, 'custom')
66
+ assert.equal(config.user.email, 'custom@example.test')
67
+ })
68
+ })
69
+
70
+ test('Git isomorphic auth callback returns GCM credentials for GitHub URLs', async () => {
71
+ const git = new KernelGit(createKernel('/tmp/pinokio'))
72
+ let requestedUrl = ''
73
+ git.getCredentialForUrl = async (url) => {
74
+ requestedUrl = url
75
+ return {
76
+ username: 'octocat',
77
+ password: 'secret-token'
78
+ }
79
+ }
80
+
81
+ const auth = await git.getIsomorphicGitAuth('https://github.com/octocat/private.git')
82
+
83
+ assert.equal(requestedUrl, 'https://github.com/octocat/private.git')
84
+ assert.deepEqual(auth, {
85
+ username: 'octocat',
86
+ password: 'secret-token'
87
+ })
88
+ })
89
+
90
+ test('Git isomorphic auth callback stays anonymous when GCM has no credential', async () => {
91
+ const git = new KernelGit(createKernel('/tmp/pinokio'))
92
+ git.getCredentialForUrl = async () => null
93
+
94
+ const auth = await git.getIsomorphicGitAuth('https://github.com/octocat/public.git')
95
+
96
+ assert.equal(auth, undefined)
97
+ })
@@ -0,0 +1,158 @@
1
+ const assert = require('node:assert/strict')
2
+ const fs = require('node:fs/promises')
3
+ const path = require('node:path')
4
+ const test = require('node:test')
5
+
6
+ const Github = require('../kernel/api/github')
7
+
8
+ test('GitHub API parses credentials without exposing token handling to shell commands', () => {
9
+ const credential = Github.parseCredentialOutput([
10
+ 'protocol=https',
11
+ 'host=github.com',
12
+ 'username=octocat',
13
+ 'password=secret-token',
14
+ '',
15
+ ].join('\n'))
16
+
17
+ assert.equal(credential.username, 'octocat')
18
+ assert.equal(credential.password, 'secret-token')
19
+ })
20
+
21
+ test('GitHub API parses common GitHub remote URL formats', () => {
22
+ assert.deepEqual(
23
+ Github.parseGithubRemote('https://github.com/octocat/hello-world.git'),
24
+ { owner: 'octocat', repo: 'hello-world', fullName: 'octocat/hello-world' }
25
+ )
26
+ assert.deepEqual(
27
+ Github.parseGithubRemote('git@github.com:octocat/hello-world.git'),
28
+ { owner: 'octocat', repo: 'hello-world', fullName: 'octocat/hello-world' }
29
+ )
30
+ assert.deepEqual(
31
+ Github.parseGithubRemote('ssh://git@github.com/octocat/hello-world.git'),
32
+ { owner: 'octocat', repo: 'hello-world', fullName: 'octocat/hello-world' }
33
+ )
34
+ assert.equal(Github.parseGithubRemote('https://example.com/octocat/hello-world.git'), null)
35
+ })
36
+
37
+ test('GitHub API parses repository names for user and organization creation', () => {
38
+ assert.deepEqual(
39
+ Github.parseRepoName('', '/tmp/hello-world'),
40
+ { owner: null, repo: 'hello-world' }
41
+ )
42
+ assert.deepEqual(
43
+ Github.parseRepoName('octo-org/hello-world.git', '/tmp/ignored'),
44
+ { owner: 'octo-org', repo: 'hello-world' }
45
+ )
46
+ assert.throws(() => Github.parseRepoName('bad owner/repo', '/tmp/ignored'), /Invalid GitHub owner/)
47
+ assert.throws(() => Github.parseRepoName('owner/bad repo', '/tmp/ignored'), /Invalid GitHub repository name/)
48
+ })
49
+
50
+ test('GitHub API builds create repository requests for user and organization targets', () => {
51
+ assert.deepEqual(
52
+ Github.buildCreateRepoRequest({
53
+ owner: null,
54
+ repo: 'hello-world',
55
+ authenticatedUser: { login: 'octocat' },
56
+ visibility: 'private'
57
+ }),
58
+ { path: '/user/repos', body: { name: 'hello-world', private: true } }
59
+ )
60
+ assert.deepEqual(
61
+ Github.buildCreateRepoRequest({
62
+ owner: 'octo-org',
63
+ repo: 'hello-world',
64
+ authenticatedUser: { login: 'octocat' },
65
+ visibility: 'public'
66
+ }),
67
+ { path: '/orgs/octo-org/repos', body: { name: 'hello-world', private: false } }
68
+ )
69
+ assert.deepEqual(
70
+ Github.buildCreateRepoRequest({
71
+ owner: 'octo-org',
72
+ repo: 'hello-world',
73
+ authenticatedUser: { login: 'octocat' },
74
+ visibility: 'internal'
75
+ }),
76
+ { path: '/orgs/octo-org/repos', body: { name: 'hello-world', visibility: 'internal' } }
77
+ )
78
+ assert.throws(
79
+ () => Github.buildCreateRepoRequest({
80
+ owner: null,
81
+ repo: 'hello-world',
82
+ authenticatedUser: { login: 'octocat' },
83
+ visibility: 'internal'
84
+ }),
85
+ /Internal repositories require an organization owner/
86
+ )
87
+ })
88
+
89
+ test('GitHub create and fork scripts no longer invoke gh repo commands', async () => {
90
+ const root = path.resolve(__dirname, '..')
91
+ const createScript = await fs.readFile(path.join(root, 'kernel/scripts/git/create'), 'utf8')
92
+ const forkScript = await fs.readFile(path.join(root, 'kernel/scripts/git/fork'), 'utf8')
93
+
94
+ assert.match(createScript, /"method": "github\.create"/)
95
+ assert.match(forkScript, /"method": "github\.fork"/)
96
+ assert.doesNotMatch(createScript, /gh repo/)
97
+ assert.doesNotMatch(forkScript, /gh repo/)
98
+ })
99
+
100
+ test('GitHub create refuses to push when origin points somewhere else', async () => {
101
+ const api = new Github()
102
+ let requests = 0
103
+ const commands = []
104
+
105
+ api.getCredential = async () => ({ password: 'secret-token' })
106
+ api.getAuthenticatedUser = async () => ({ login: 'octocat' })
107
+ api.remoteUrl = async () => 'https://github.com/octocat/old-repo.git'
108
+ api.githubRequest = async () => {
109
+ requests += 1
110
+ return {}
111
+ }
112
+ api.runGit = async (args) => {
113
+ commands.push(args)
114
+ }
115
+
116
+ await assert.rejects(
117
+ () => api.create({
118
+ params: {
119
+ cwd: '/tmp/repo',
120
+ name: 'octocat/new-repo',
121
+ visibility: 'public'
122
+ }
123
+ }),
124
+ /origin already points at https:\/\/github\.com\/octocat\/old-repo\.git/
125
+ )
126
+
127
+ assert.equal(requests, 0)
128
+ assert.deepEqual(commands, [])
129
+ })
130
+
131
+ test('GitHub fork does not rename or repoint origin', async () => {
132
+ const api = new Github()
133
+ const commands = []
134
+
135
+ api.remoteUrl = async (_cwd, name) => {
136
+ if (name === 'origin') return 'https://github.com/source-org/project.git'
137
+ return ''
138
+ }
139
+ api.runGit = async (args) => {
140
+ commands.push(args)
141
+ }
142
+
143
+ await api.configureForkRemote({
144
+ cwd: '/tmp/repo',
145
+ kernel: {},
146
+ source: { owner: 'source-org', repo: 'project', fullName: 'source-org/project' },
147
+ forkRepo: {
148
+ owner: { login: 'octocat' },
149
+ name: 'project',
150
+ full_name: 'octocat/project',
151
+ clone_url: 'https://github.com/octocat/project.git'
152
+ }
153
+ })
154
+
155
+ assert.deepEqual(commands, [
156
+ ['remote', 'add', 'octocat', 'https://github.com/octocat/project.git']
157
+ ])
158
+ })
@@ -0,0 +1,117 @@
1
+ const assert = require('node:assert/strict')
2
+ const fs = require('node:fs/promises')
3
+ const os = require('node:os')
4
+ const path = require('node:path')
5
+ const test = require('node:test')
6
+
7
+ const Server = require('../server')
8
+
9
+ test('GitHub connection ignores legacy gh hosts when no GCM account exists', async () => {
10
+ const connection = await Server.prototype.get_github_connection.call({
11
+ get_legacy_github_hosts: async () => [
12
+ 'github.com:',
13
+ ' git_protocol: https',
14
+ ' users:',
15
+ ' cocktailpeanut:',
16
+ ' user: cocktailpeanut',
17
+ '',
18
+ ].join('\n'),
19
+ github_gcm: async () => '',
20
+ })
21
+
22
+ assert.equal(connection.connected, false)
23
+ assert.equal(connection.provider, null)
24
+ assert.equal(connection.display, '')
25
+ assert.match(connection.legacyHosts, /cocktailpeanut/)
26
+ })
27
+
28
+ test('GitHub connection reports GCM accounts as connected', async () => {
29
+ const connection = await Server.prototype.get_github_connection.call({
30
+ get_legacy_github_hosts: async () => '',
31
+ github_gcm: async () => 'octocat\n',
32
+ })
33
+
34
+ assert.equal(connection.connected, true)
35
+ assert.equal(connection.provider, 'gcm')
36
+ assert.equal(connection.display, 'github.com: octocat')
37
+ })
38
+
39
+ test('GitHub connection coalesces only concurrent GCM checks', async () => {
40
+ let calls = 0
41
+ const context = {
42
+ get_legacy_github_hosts: async () => '',
43
+ github_gcm: async () => {
44
+ calls += 1
45
+ await new Promise((resolve) => setTimeout(resolve, 10))
46
+ return 'octocat\n'
47
+ },
48
+ }
49
+
50
+ const [first, second] = await Promise.all([
51
+ Server.prototype.get_github_connection.call(context),
52
+ Server.prototype.get_github_connection.call(context),
53
+ ])
54
+
55
+ assert.equal(calls, 1)
56
+ assert.equal(first.connected, true)
57
+ assert.deepEqual(second, first)
58
+
59
+ await Server.prototype.get_github_connection.call(context)
60
+ assert.equal(calls, 2)
61
+ })
62
+
63
+ test('GitHub login only emits completion marker after credential verification', () => {
64
+ const { doneMarker, message } = Server.prototype.github_login_params.call({
65
+ kernel: { platform: 'darwin' }
66
+ })
67
+
68
+ assert.equal(doneMarker, 'PINOKIO_GITHUB_LOGIN_DONE')
69
+ assert.match(message, /git credential-manager github login --web --force && printf 'protocol=https\\nhost=github\.com\\n\\n' \| GIT_TERMINAL_PROMPT=0 GCM_INTERACTIVE=never git credential fill >\/dev\/null && \(GCM_DONE=INOKIO_GITHUB_LOGIN_DONE; printf 'P%s\\n' "\$GCM_DONE"\)/)
70
+ assert.doesNotMatch(message, /git credential-manager github login --web --force\s*;/)
71
+ assert.doesNotMatch(message, /PINOKIO_GITHUB_LOGIN_DONE/)
72
+ })
73
+
74
+ test('GitHub login uses success-only credential verification on Windows', () => {
75
+ const { message } = Server.prototype.github_login_params.call({
76
+ kernel: { platform: 'win32' }
77
+ })
78
+
79
+ assert.match(message, /git credential-manager github login --web --force && cmd \/C "set GIT_TERMINAL_PROMPT=0&& set GCM_INTERACTIVE=never&& \(echo protocol=https& echo host=github\.com& echo\.\) \| git credential fill >NUL" && echo P\^INOKIO_GITHUB_LOGIN_DONE/)
80
+ assert.doesNotMatch(message, /PINOKIO_GITHUB_LOGIN_DONE/)
81
+ })
82
+
83
+ test('GitHub logout clears legacy gh auth even when GCM accounts exist', async () => {
84
+ let clearedLegacyAuth = false
85
+ const context = {
86
+ kernel: { platform: 'darwin' },
87
+ github_logout_command: Server.prototype.github_logout_command,
88
+ clear_legacy_github_auth: async () => {
89
+ clearedLegacyAuth = true
90
+ }
91
+ }
92
+
93
+ const result = await Server.prototype.github_logout_params.call(context, {
94
+ accounts: ['octocat'],
95
+ legacyHosts: 'github.com:\n user: octocat\n'
96
+ })
97
+
98
+ assert.equal(clearedLegacyAuth, true)
99
+ assert.equal(result.hadLegacyAuth, true)
100
+ assert.equal(result.message, 'git credential-manager github logout octocat')
101
+ })
102
+
103
+ test('GitHub legacy auth cleanup removes old gh hosts file', async () => {
104
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-gh-auth-'))
105
+ const hostsFile = path.join(dir, 'config/gh/hosts.yml')
106
+ await fs.mkdir(path.dirname(hostsFile), { recursive: true })
107
+ await fs.writeFile(hostsFile, 'github.com:\n user: octocat\n')
108
+
109
+ await Server.prototype.clear_legacy_github_auth.call({
110
+ kernel: {
111
+ path: (relativePath) => path.join(dir, relativePath)
112
+ }
113
+ })
114
+
115
+ await assert.rejects(() => fs.stat(hostsFile), { code: 'ENOENT' })
116
+ await fs.rm(dir, { recursive: true, force: true })
117
+ })
@@ -0,0 +1,25 @@
1
+ const assert = require('node:assert/strict')
2
+ const test = require('node:test')
3
+
4
+ const Huggingface = require('../kernel/bin/huggingface')
5
+
6
+ function createHuggingface(version) {
7
+ const huggingface = new Huggingface()
8
+ huggingface.kernel = {
9
+ bin: {
10
+ installed: {
11
+ conda: new Set(version ? ['huggingface_hub'] : []),
12
+ conda_versions: version ? { huggingface_hub: version } : {},
13
+ },
14
+ },
15
+ }
16
+ return huggingface
17
+ }
18
+
19
+ test('Huggingface.installed accepts exactly huggingface_hub 1.0.1', async () => {
20
+ assert.equal(await createHuggingface('1.0.1').installed(), true)
21
+ assert.equal(await createHuggingface('1.0.1-pyhd8ed1ab_0').installed(), true)
22
+ assert.equal(await createHuggingface('1.0.2').installed(), false)
23
+ assert.equal(await createHuggingface('0.35.3').installed(), false)
24
+ assert.equal(await createHuggingface(null).installed(), false)
25
+ })